-
+
{children}
diff --git a/components/submit/index.js b/components/submit/index.js
new file mode 100644
index 00000000..1cdf16e4
--- /dev/null
+++ b/components/submit/index.js
@@ -0,0 +1,32 @@
+import React from 'react'
+import Link from 'next/link'
+import { Type, Box, Button } from 'blockstack-ui'
+
+const SuccessCard = ({ isAppMiningEligible }) => (
+
+
+
+ Success!
+
+
+
+
+ Thanks for your submission! Your app will need to be approved before being public on app.co.
+
+ {isAppMiningEligible && (
+ <>
+
+ To update your app's details and enroll in App Mining, visit our Maker Portal
+
+
+
+
+ >
+ )}
+
+
+)
+
+export default SuccessCard
diff --git a/package.json b/package.json
index f1e36928..37ac0eaa 100644
--- a/package.json
+++ b/package.json
@@ -37,6 +37,7 @@
"@mdx-js/tag": "^0.15.0",
"@next/bundle-analyzer": "^9.0.6",
"@next/mdx": "^9.0.6",
+ "@types/react-redux": "^7.1.5",
"accounting": "^0.4.1",
"async": "^2.6.0",
"bitcoinjs-lib": "^5.0.5",
@@ -63,6 +64,7 @@
"grid-styled": "5.0.2",
"intersection-observer": "^0.5.1",
"isomorphic-unfetch": "^3.0.0",
+ "js-cookie": "^2.2.1",
"lodash": "^4.17.11",
"lodash.debounce": "^4.0.8",
"lru-cache": "^4.1.3",
diff --git a/pages/admin/app.js b/pages/admin/app.js
index 1cadeb4b..540075ff 100644
--- a/pages/admin/app.js
+++ b/pages/admin/app.js
@@ -88,7 +88,7 @@ class App extends React.Component {
async fetchApp() {
const qs = queryString.parse(document.location.search)
if (qs.id && this.props.jwt) {
- const request = await fetch(`${this.propsapiServer}/api/admin/apps/${qs.id}`, {
+ const request = await fetch(`${this.props.apiServer}/api/admin/apps/${qs.id}`, {
method: 'GET',
headers: new Headers({
Authorization: `Bearer ${this.props.jwt}`,
@@ -151,10 +151,6 @@ class App extends React.Component {
// const app = this.props.selectedApp
// const { name } = this.state;
const app = this.state
-<<<<<<< HEAD
- const { categories, authentications, storageNetworks, blockchains } = this.props
-=======
->>>>>>> origin/develop
if (!app.name) {
return Loading
}
@@ -210,6 +206,10 @@ class App extends React.Component {
Magic link: {`/maker/${app.accessToken}`}
+
+ Maker portal: {`/maker/apps/${app.id}`}
+
+
-
-
-
-
-
-
- )
- }
-}
+const AllAppsPage = () => (
+
+
+
+
+
+
+
+)
export default AllAppsPage
diff --git a/pages/maker/apps/index.js b/pages/maker/apps/index.js
new file mode 100644
index 00000000..64e30c92
--- /dev/null
+++ b/pages/maker/apps/index.js
@@ -0,0 +1,122 @@
+import React from 'react'
+import { useRouter } from 'next/router'
+import { Flex, Box, Type } from 'blockstack-ui'
+import { connect } from 'react-redux'
+import isNaN from 'lodash/isNaN'
+
+import { selectMaker, selectAppList, selectCurrentApp, selectCompetionStatus } from '@stores/maker/selectors'
+import { fetchApps, selectAppAction } from '@stores/maker/actions'
+import { selectApiServer, selectUser } from '@stores/apps/selectors'
+import { MakerContainer, MakerContentBox, MakerStickyStatusBox } from '@components/maker/styled'
+import { Page } from '@components/page'
+import Head from '@containers/head'
+import Maker from '@components/maker'
+
+const mapStateToProps = (state) => ({
+ user: selectUser(state),
+ apiServer: selectApiServer(state),
+ maker: selectMaker(state),
+ appList: selectAppList(state),
+ selectedApp: selectCurrentApp(state),
+ completionStatus: selectCompetionStatus(state)
+})
+
+const handleChangingApp = (event, fn) => (dispatch) => {
+ event.persist()
+ const id = event.target.value
+ dispatch(selectAppAction(id))
+ fn(id)
+}
+
+const getAppId = (params) => {
+ if (!params) return undefined
+ const id = parseInt(params.appId, 10)
+ if (isNaN(id)) return undefined
+ return id
+}
+
+const LoadingPage = ({ message = 'Loading...' }) => (
+
+
+
+
+ {message}
+
+
+
+
+)
+
+const MakerPortal = connect()(({ maker, selectedApp, appList, appId, apiServer, completionStatus, user, dispatch }) => {
+ const router = useRouter()
+
+ const updateMakerRoute = (id) =>
+ router.push('/maker/apps', `/maker/apps/${id}`, {
+ shallow: true
+ })
+
+ if (maker.loading || !selectedApp) return
+
+ return (
+
+
+
+
+ {appList.length && (
+
+ )}
+
+
+ {selectedApp.name}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+})
+
+MakerPortal.getInitialProps = async ({ req, reduxStore }) => {
+ const { params, universalCookies } = req
+ const userCookie = universalCookies.cookies.jwt
+ const appId = getAppId(params)
+ const apiServer = selectApiServer(reduxStore.getState())
+ await fetchApps({ apiServer, user: { jwt: userCookie } })(reduxStore.dispatch)
+ let selectedApp = {}
+ if (appId) {
+ reduxStore.dispatch(selectAppAction(appId))
+ selectedApp = selectCurrentApp(reduxStore.getState())
+ } else {
+ const firstApp = selectAppList(reduxStore.getState())[0]
+ selectedApp = firstApp
+ }
+ const props = mapStateToProps(reduxStore.getState())
+
+ return { appId, ...props, selectedApp, dispatch: reduxStore.dispatch }
+}
+
+export default MakerPortal
diff --git a/pages/maker/index.js b/pages/maker/index.js
deleted file mode 100644
index fb8fd053..00000000
--- a/pages/maker/index.js
+++ /dev/null
@@ -1,98 +0,0 @@
-import React from 'react'
-import { Flex, Box, Type } from 'blockstack-ui'
-import { connect } from 'react-redux'
-
-import { selectApiServer, selectUser } from '@stores/apps/selectors'
-import { Page } from '@components/page'
-import Head from '@containers/head'
-import Maker from '@components/maker'
-import { MakerContainer, MakerContentBox, MakerStickyStatusBox } from '@components/maker/styled'
-
-class MakerPortal extends React.Component {
-
- state = {
- loading: true,
- apps: [],
- errorMessage: null
- }
-
- componentDidMount() {
- this.fetchApps()
- }
-
- async fetchApps() {
- const { apiServer, user } = this.props
- if (!(user && user.jwt)) {
- this.setState({
- loading: false,
- errorMessage: 'Please sign in to access the maker portal.'
- })
- return
- }
-
- const uri = `${apiServer}/api/maker/apps`
- const response = await fetch(uri, {
- headers: {
- Authorization: `Bearer ${user.jwt}`
- }
- })
- const { apps } = await response.json()
- this.setState({
- loading: false,
- apps
- })
- }
-
- render() {
- const { apiServer, user } = this.props
-
- const { loading, errorMessage, apps } = this.state
- const [app] = apps // TODO: this is for now until we handle multiple apps
-
- if (loading || errorMessage) {
- return (
-
-
-
- {loading ? 'Loading...' : errorMessage}
-
-
-
- )
- }
-
- return (
-
-
-
-
- {app.name}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- )
- }
-}
-
-const mapStateToProps = (state) => ({
- user: selectUser(state),
- apiServer: selectApiServer(state)
-})
-
-export default connect(mapStateToProps)(MakerPortal)
diff --git a/pages/maker/magic-link.js b/pages/maker/magic-link.js
index 02563bc1..1b72c72d 100644
--- a/pages/maker/magic-link.js
+++ b/pages/maker/magic-link.js
@@ -14,6 +14,7 @@ class MakerMagicLink extends React.Component {
const { accessToken } = query
const apiServer = selectApiServer(reduxStore.getState())
const appResult = await fetch(`${apiServer}/api/magic-link/${accessToken}`)
+ console.log(appResult)
const { app } = await appResult.json()
return {
@@ -85,7 +86,8 @@ class MakerMagicLink extends React.Component {
{app.name} is now owned by {app.adminBlockstackId || user.user.blockstackUsername}
- Your Magic Link is now no longer functional. Instead, you will use your Blockstack ID to sign in to the developer portal.
+ Your Magic Link is now no longer functional. Instead, you will use your Blockstack ID to sign in to
+ the developer portal.
@@ -97,9 +99,12 @@ class MakerMagicLink extends React.Component {
Sign in with Blockstack to claim {app.name}
- You will use this Blockstack ID to make changes to your app, and remove or modify your listing in the future.
+ You will use this Blockstack ID to make changes to your app, and remove or modify your listing in
+ the future.
-
+
>
)}
@@ -117,4 +122,7 @@ const mapStateToProps = (state) => ({
const mapDispatchToProps = (dispatch) => bindActionCreators({ ...UserStore.actions }, dispatch)
-export default connect(mapStateToProps, mapDispatchToProps)(MakerMagicLink)
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(MakerMagicLink)
diff --git a/pages/submit.js b/pages/submit.js
index 1274151f..c65f58ba 100644
--- a/pages/submit.js
+++ b/pages/submit.js
@@ -2,13 +2,13 @@ import React from 'react'
import 'isomorphic-unfetch'
import { connect } from 'react-redux'
import Head from '@containers/head'
-import Link from 'next/link'
import { bindActionCreators } from 'redux'
+import { MakerTitle, MakerCardHeader, MakerButton } from '@components/maker/styled'
import { Page } from '@components/page'
-import { Type, Field, Flex, Box, Button } from 'blockstack-ui'
+import { Type, Field, Flex, Box } from 'blockstack-ui'
import { selectAppConstants, selectApiServer } from '@stores/apps/selectors'
-import { string, boolean } from 'yup'
-import { FormSection, ErrorMessage, Bar, sections as getSections } from '@containers/submit'
+import { FormSection, ErrorMessage, sections as getSections } from '@containers/submit'
+import SuccessCard from '@components/submit'
import debounce from 'lodash.debounce'
import UserStore from '@stores/user'
@@ -92,13 +92,8 @@ const Submit = ({ appConstants, setState, state, errors, submit, user, loading,
return (
- Add an app to App.co
-
- Add any user-ready decentralized app: It could be an app you built, or an app you discovered. We manually
- verify all information before publishing to App.co. Contact details are not displayed and we promise to keep
- your information private and safe.
-
-
+ Submit your app
+ Personal details
{user && user.user && (
@@ -121,19 +116,16 @@ const Submit = ({ appConstants, setState, state, errors, submit, user, loading,
)}
@@ -263,51 +255,27 @@ class SubmitDapp extends React.Component {
const { appConstants } = this.props
return (
-
+
-
- {this.state.success ? (
- <>
-
-
-
- Success!
-
-
-
- Thanks for your submission! Your app will need to be approved before being public on app.co.
- {this.appMiningEligible() && (
- <>
- To update your app's details and enroll in App Mining, visit our Maker Portal
-
-
-
- >
- )}
-
-
- >
- ) : (
- this.setState(args), 100)}
- state={this.state.values}
- errors={this.state.errorCount > 0 && this.state.errors}
- signIn={this.props.signIn}
- user={this.props.user}
- isAppMiningEligible={this.appMiningEligible()}
- />
- )}
+
+ {
+ this.state.success
+ ?
+ : (
+ this.setState(args), 100)}
+ state={this.state.values}
+ errors={this.state.errorCount > 0 && this.state.errors}
+ signIn={this.props.signIn}
+ user={this.props.user}
+ isAppMiningEligible={this.appMiningEligible()}
+ />
+ )
+ }
)
diff --git a/server.js b/server.js
index 4f9ae9b5..9014fb82 100644
--- a/server.js
+++ b/server.js
@@ -66,7 +66,8 @@ async function renderAndCache(req, res, pagePath, serverData) {
...serverData,
blockstackRankedApps,
appMiningMonths,
- appMiningApps
+ appMiningApps,
+ params: req.params
}
const html = await app.renderToHTML(req, res, pagePath, dataToPass)
if (cacheKey) {
@@ -220,7 +221,9 @@ app.prepare().then(() => {
/**
* Maker pages
*/
- server.get('/maker', (req, res) => renderAndCache(req, res, '/maker'))
+ server.get('/maker', (req, res) => res.redirect('/maker/apps'))
+ server.get('/maker/apps', (req, res) => renderAndCache(req, res, '/maker/apps'))
+ server.get('/maker/apps/:appId', (req, res) => renderAndCache(req, res, '/maker/apps'))
server.get('/maker/:accessToken', (req, res) => renderAndCache(req, res, '/maker/magic-link', { accessToken: req.params.accessToken }))
apps.platforms.forEach((platform) => {
diff --git a/stores/index.js b/stores/index.js
index 6c24d38a..ee8a36d5 100644
--- a/stores/index.js
+++ b/stores/index.js
@@ -8,6 +8,7 @@ import newsletter from '@stores/newsletter'
import RouterStore from '@stores/router'
import makeMiningReducer from '@stores/mining/reducer'
import AdminMiningReducer from '@stores/mining-admin/reducer'
+import makerReducer from '@stores/maker/reducer'
export default (data) => {
const composeEnhancers = (typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose
@@ -21,7 +22,8 @@ export default (data) => {
newsletter,
router: RouterStore.reducer,
mining: makeMiningReducer(data),
- miningAdmin: AdminMiningReducer
+ miningAdmin: AdminMiningReducer,
+ maker: makerReducer
})
return finalCreateStore(Reducer)
diff --git a/stores/maker/actions.js b/stores/maker/actions.js
new file mode 100644
index 00000000..a2452c54
--- /dev/null
+++ b/stores/maker/actions.js
@@ -0,0 +1,78 @@
+export const SET_LOADING_DONE = 'maker-page/SET_LOADING_DONE'
+export const MAKER_AUTH_ERROR = 'maker-page/MAKER_AUTH_ERROR'
+
+export const SET_PAYMENT_DETAILS_COMPLETE = 'maker-page/SET_PAYMENT_DETAILS_COMPLETE'
+export const SAVE_PAYMENT_DETAILS = 'maker-page/SAVE_PAYMENT_DETAILS'
+export const SAVE_PAYMENT_DETAILS_DONE = 'maker-page/SAVE_PAYMENT_DETAILS_DONE'
+export const SAVE_PAYMENT_DETAILS_FAIL = 'maker-page/SAVE_PAYMENT_DETAILS_FAIL'
+
+export const SET_KYC_COMPLETE = 'maker-page/SET_KYC_COMPLETE'
+export const SET_LEGAL_COMPLETE = 'maker-page/SET_LEGAL_COMPLETE'
+
+export const FETCH_APPS = 'maker-page/FETCH_APPS'
+export const FETCH_APPS_DONE = 'maker-page/FETCH_APPS_DONE'
+export const FETCH_APPS_FAIL = 'maker-page/FETCH_APPS_FAIL'
+
+export const SELECT_APP = 'maker-page/SELECT_APP'
+
+export const errorAction = () => ({ type: MAKER_AUTH_ERROR })
+export const setLoadingDoneAction = () => ({ type: SET_LOADING_DONE })
+export const savePaymentDetailsAction = () => ({ type: SAVE_PAYMENT_DETAILS })
+export const savePaymentDetailsDoneAction = (addresses) => ({
+ type: SAVE_PAYMENT_DETAILS_DONE,
+ payload: addresses
+})
+export const savePaymentDetailsFailAction = () => ({ type: SAVE_PAYMENT_DETAILS_FAIL })
+
+export const setKycComplete = () => ({ type: SET_KYC_COMPLETE })
+export const setLegalComplete = () => ({ type: SET_LEGAL_COMPLETE })
+
+export const fetchAppsAction = () => ({ type: FETCH_APPS })
+
+export const fetchAppsDoneAction = (payload) => ({ type: FETCH_APPS_DONE, payload })
+
+export const fetchAppsFailAction = () => ({ type: FETCH_APPS_FAIL })
+
+export const selectAppAction = (payload) => ({ type: SELECT_APP, payload })
+
+export const savePaymentDetails = ({ apiServer, appId, jwt, btcAddress, stxAddress }) => async (dispatch) => {
+ dispatch(savePaymentDetailsAction())
+ try {
+ const response = await fetch(`${apiServer}/api/maker/apps?appId=${appId}`, {
+ method: 'POST',
+ headers: new Headers({
+ 'Content-Type': 'application/json',
+ authorization: `Bearer ${jwt}`
+ }),
+ body: JSON.stringify({
+ BTCAddress: btcAddress,
+ stacksAddress: stxAddress
+ })
+ })
+ await response.json()
+ dispatch(savePaymentDetailsDoneAction({ appId, btcAddress, stxAddress }))
+ } catch (error) {
+ dispatch(savePaymentDetailsFailAction(error))
+ }
+}
+
+export const fetchApps = ({ user, apiServer }) => async (dispatch) => {
+ if (!(user && user.jwt)) {
+ dispatch(errorAction())
+ return
+ }
+
+ dispatch(fetchAppsAction())
+ try {
+ const uri = `${apiServer}/api/maker/apps`
+ const response = await fetch(uri, {
+ headers: {
+ Authorization: `Bearer ${user.jwt}`
+ }
+ })
+ const data = await response.json()
+ await dispatch(fetchAppsDoneAction(data))
+ } catch (error) {
+ dispatch(fetchAppsFailAction())
+ }
+}
diff --git a/stores/maker/reducer.js b/stores/maker/reducer.js
new file mode 100644
index 00000000..69145591
--- /dev/null
+++ b/stores/maker/reducer.js
@@ -0,0 +1,51 @@
+import keyBy from 'lodash/keyBy'
+import * as MakerActions from './actions'
+
+const initialState = {
+ loading: true,
+ apps: [],
+ appIds: [],
+ appEntities: {},
+ selectedAppId: null,
+ errorMessage: null
+}
+
+function makerReducer(state = initialState, action) {
+ switch (action.type) {
+ case MakerActions.MAKER_AUTH_ERROR:
+ return {
+ ...state,
+ loading: false,
+ errorMessage: 'Please sign in to access the maker portal.'
+ }
+
+ case MakerActions.FETCH_APPS_DONE:
+ return {
+ ...state,
+ loading: false,
+ appIds: action.payload.apps.map((app) => app.id),
+ appEntities: keyBy(action.payload.apps, 'id')
+ }
+
+ case MakerActions.SELECT_APP:
+ return { ...state, selectedAppId: action.payload }
+
+ case MakerActions.SAVE_PAYMENT_DETAILS_DONE:
+ return {
+ ...state,
+ appEntities: {
+ ...state.appEntities,
+ [action.payload.appId]: {
+ ...state.appEntities[action.payload.appId],
+ BTCAddress: action.payload.btcAddress,
+ stacksAddress: action.payload.stxAddress
+ }
+ }
+ }
+
+ default:
+ return state
+ }
+}
+
+export default makerReducer
diff --git a/stores/maker/selectors.js b/stores/maker/selectors.js
new file mode 100644
index 00000000..966940f0
--- /dev/null
+++ b/stores/maker/selectors.js
@@ -0,0 +1,30 @@
+const updateEntity = (state, id, newProps) => ({
+ ...state,
+ appEntities: {
+ ...state.appEntities,
+ [id]: {
+ ...state.appEntities[id],
+ ...newProps
+ }
+ }
+})
+
+export const selectMaker = (state) => state.maker
+
+export const selectIsMakerLoading = (state) => state.maker.loading
+
+export const selectAppList = (state) => state.maker.appIds.map((id) => state.maker.appEntities[id])
+
+export const selectCurrentApp = (state) => state.maker.appEntities[state.maker.selectedAppId]
+
+export const selectCompetionStatus = (state) => {
+ const selectedApp = state.maker.appEntities[state.maker.selectedAppId]
+ if (!selectedApp) {
+ return {}
+ }
+ return {
+ paymentDetailsComplete: selectedApp.BTCAddress && selectedApp.stacksAddress,
+ kycComplete: selectedApp.isKYCVerified,
+ legalComplete: false
+ }
+}
diff --git a/stores/user/index.js b/stores/user/index.js
index dd1efe05..c85b2db7 100644
--- a/stores/user/index.js
+++ b/stores/user/index.js
@@ -1,4 +1,5 @@
import { UserSession, AppConfig } from 'blockstack'
+import Cookies from 'js-cookie'
const host = typeof document === 'undefined' ? 'https://app.co' : document.location.origin
const appConfig = new AppConfig(['store_write'], host)
@@ -24,35 +25,43 @@ const signingIn = () => ({
const signedIn = (data) => ({
type: constants.SIGNED_IN,
- token: data.token,
- user: data.user,
- userId: data.user.id
-})
-
-const signOut = () => ({
- type: constants.SIGNING_OUT
+ payload: {
+ token: data.token,
+ user: data.user,
+ userId: data.user.id
+ }
})
-const handleSignIn = (apiServer) =>
- async function innerHandleSignIn(dispatch) {
- const token = userSession.getAuthResponseToken()
- if (!token) {
- return true
- }
- if (userSession.isUserSignedIn()) {
- userSession.signUserOut()
- }
+const signOut = () => {
+ Cookies.remove('jwt')
+ return {
+ type: constants.SIGNING_OUT
+ }
+}
- dispatch(signingIn())
- await userSession.handlePendingSignIn()
- const url = `${apiServer}/api/authenticate?authToken=${token}`
- const response = await fetch(url, {
- method: 'POST'
- })
- const json = await response.json()
- dispatch(signedIn(json))
+const handleSignIn = (apiServer) => async (dispatch) => {
+ const token = userSession.getAuthResponseToken()
+ if (!token) {
return true
}
+ if (userSession.isUserSignedIn()) {
+ userSession.signUserOut()
+ }
+
+ dispatch(signingIn())
+ await userSession.handlePendingSignIn()
+ const url = `${apiServer}/api/authenticate?authToken=${token}`
+ const response = await fetch(url, {
+ method: 'POST'
+ })
+ const json = await response.json()
+ dispatch(signedIn(json))
+ const cookie = Cookies.get('jwt')
+ if (!cookie) {
+ Cookies.set('jwt', json.token)
+ }
+ return true
+}
const signIn = (redirectPath = 'admin') => {
const redirect = `${window.location.origin}/${redirectPath}`
@@ -67,25 +76,23 @@ const actions = {
signOut
}
-const reducer = (state = initialState, action) => {
- switch (action.type) {
+const reducer = (state = initialState, { type, payload }) => {
+ switch (type) {
case constants.SIGNING_IN:
- return Object.assign({}, state, {
+ return {
+ ...state,
signingIn: true
- })
+ }
case constants.SIGNED_IN:
- return Object.assign({}, state, {
+ return {
+ ...state,
signingIn: false,
- jwt: action.token,
- userId: action.userId,
- user: action.user
- })
+ jwt: payload.token,
+ userId: payload.userId,
+ user: payload.user
+ }
case constants.SIGNING_OUT:
- return Object.assign({}, state, {
- user: null,
- userId: null,
- jwt: null
- })
+ return { ...state, user: null, userId: null, jwt: null }
default:
return state
}
diff --git a/yarn.lock b/yarn.lock
index b77b2a98..d5ee2cb8 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2098,6 +2098,14 @@
dependencies:
"@types/bn.js" "*"
+"@types/hoist-non-react-statics@^3.3.0":
+ version "3.3.1"
+ resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
+ integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
+ dependencies:
+ "@types/react" "*"
+ hoist-non-react-statics "^3.3.0"
+
"@types/node@*":
version "10.12.24"
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.24.tgz#b13564af612a22a20b5d95ca40f1bffb3af315cf"
@@ -2118,6 +2126,24 @@
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.2.tgz#0e58ae66773d7fd7c372a493aff740878ec9ceaa"
integrity sha512-f8JzJNWVhKtc9dg/dyDNfliTKNOJSLa7Oht/ElZdF/UbMUmAH3rLmAk3ODNjw0mZajDEgatA03tRjB4+Dp/tzA==
+"@types/react-redux@^7.1.5":
+ version "7.1.5"
+ resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.5.tgz#c7a528d538969250347aa53c52241051cf886bd3"
+ integrity sha512-ZoNGQMDxh5ENY7PzU7MVonxDzS1l/EWiy8nUhDqxFqUZn4ovboCyvk4Djf68x6COb7vhGTKjyjxHxtFdAA5sUA==
+ dependencies:
+ "@types/hoist-non-react-statics" "^3.3.0"
+ "@types/react" "*"
+ hoist-non-react-statics "^3.3.0"
+ redux "^4.0.0"
+
+"@types/react@*":
+ version "16.9.11"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.11.tgz#70e0b7ad79058a7842f25ccf2999807076ada120"
+ integrity sha512-UBT4GZ3PokTXSWmdgC/GeCGEJXE5ofWyibCcecRLUVN2ZBpXQGVgQGtG2foS7CrTKFKlQVVswLvf7Js6XA/CVQ==
+ dependencies:
+ "@types/prop-types" "*"
+ csstype "^2.2.0"
+
"@types/react@^16.9.2":
version "16.9.2"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.2.tgz#6d1765431a1ad1877979013906731aae373de268"
@@ -6628,7 +6654,7 @@ hoist-non-react-statics@^2.3.1, hoist-non-react-statics@^2.5.0, hoist-non-react-
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47"
integrity sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==
-hoist-non-react-statics@^3.1.0:
+hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz#b09178f0122184fb95acf525daaecb4d8f45958b"
integrity sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==
@@ -7738,6 +7764,11 @@ jest@^22.4.3:
resolved "https://registry.yarnpkg.com/jquery/-/jquery-2.2.4.tgz#2c89d6889b5eac522a7eea32c14521559c6cbf02"
integrity sha1-LInWiJterFIqfuoywUUhVZxsvwI=
+js-cookie@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8"
+ integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==
+
js-levenshtein@^1.1.3:
version "1.1.6"
resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"
@@ -10794,6 +10825,14 @@ redux@^3.7.2:
loose-envify "^1.1.0"
symbol-observable "^1.0.3"
+redux@^4.0.0:
+ version "4.0.4"
+ resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.4.tgz#4ee1aeb164b63d6a1bcc57ae4aa0b6e6fa7a3796"
+ integrity sha512-vKv4WdiJxOWKxK0yRoaK3Y4pxxB0ilzVx6dszU2W8wLxlb2yikRph4iV/ymtdJ6ZxpBLFbyrxklnT5yBbQSl3Q==
+ dependencies:
+ loose-envify "^1.4.0"
+ symbol-observable "^1.2.0"
+
redux@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.1.tgz#436cae6cc40fbe4727689d7c8fae44808f1bfef5"