diff --git a/package.json b/package.json index f00a292..b79e871 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "redux": "^4.0.5", "redux-logger": "^3.0.6", "redux-persist": "^6.0.0", + "redux-thunk": "^2.3.0", "reselect": "^4.0.0", "styled-components": "^5.2.3", "web-vitals": "^0.2.4" diff --git a/src/components/collections-overview/collections-overview.container.jsx b/src/components/collections-overview/collections-overview.container.jsx new file mode 100644 index 0000000..7f28365 --- /dev/null +++ b/src/components/collections-overview/collections-overview.container.jsx @@ -0,0 +1,17 @@ +import { connect } from 'react-redux'; +import { compose } from 'redux'; +import { createStructuredSelector } from 'reselect'; +import { selectIsCollectionFetching } from '../../redux/shop/shop.selector'; +import WithSpinner from '../with-spinner/with-spinner.component'; +import CollectionOverview from './collections-overview.component'; + +const mapStateToProps = createStructuredSelector({ + isLoading: selectIsCollectionFetching +}) + +const CollectionOverviewContainer = compose( + connect(mapStateToProps), + WithSpinner +)(CollectionOverview); + +export default CollectionOverviewContainer; \ No newline at end of file diff --git a/src/components/header/header.component.jsx b/src/components/header/header.component.jsx index 3f4f642..dfe4b3b 100644 --- a/src/components/header/header.component.jsx +++ b/src/components/header/header.component.jsx @@ -20,10 +20,10 @@ const Header = ({currentUser, hidden}) => ( - + SHOP - + CONTACT { diff --git a/src/pages/collection/collection.conatiner.jsx b/src/pages/collection/collection.conatiner.jsx new file mode 100644 index 0000000..d3e7595 --- /dev/null +++ b/src/pages/collection/collection.conatiner.jsx @@ -0,0 +1,18 @@ +import { connect } from 'react-redux'; +import { compose } from 'redux'; +import { createStructuredSelector } from 'reselect'; + +import { selectIsCollectionsLoaded } from '../../redux/shop/shop.selector'; +import WithSpinner from '../../components/with-spinner/with-spinner.component'; +import CollectionPage from '../collection/collection.component'; + +const mapStateToProps = createStructuredSelector ({ + isLoading: state=> !selectIsCollectionsLoaded(state) +}) + +const CollectionPageContainer = compose( + connect(mapStateToProps), + WithSpinner +)(CollectionPage); + +export default CollectionPageContainer; \ No newline at end of file diff --git a/src/pages/shop/shop.component.jsx b/src/pages/shop/shop.component.jsx index d19c949..4260313 100644 --- a/src/pages/shop/shop.component.jsx +++ b/src/pages/shop/shop.component.jsx @@ -2,50 +2,34 @@ import React from 'react'; import { Route } from 'react-router-dom'; import {connect} from 'react-redux'; -import CollectionOverview from '../../components/collections-overview/collections-overview.component'; -import CollectionPage from '../collection/collection.component'; +import CollectionOverviewContainer from '../../components/collections-overview/collections-overview.container'; +import CollectionPageContainer from '../collection/collection.conatiner'; -import {firestore, convertCollectionsSnapshotToMap} from '../../fireabase/firebase.utils'; -import { updateCollections } from '../../redux/shop/shop.actions'; -import WithSpinner from '../../components/with-spinner/with-spinner.component'; -const CollectionsOverviewWithSpinner = WithSpinner(CollectionOverview); -const CollectionPageWithSpinner = WithSpinner(CollectionPage); +import { fetchCollectionsStartAsync } from '../../redux/shop/shop.actions'; -class ShopPage extends React.Component { - state = { - loading: true - } - unsubscribeFromSnapshot = null; +class ShopPage extends React.Component { componentDidMount() { - const {updateCollections} = this.props; - const collectionRef = firestore.collection('collections'); - - //this.unsubscribeFromSnapshot = - - collectionRef.get().then( snapshot=>{ - const collectionsMap = convertCollectionsSnapshotToMap(snapshot); - updateCollections(collectionsMap); - this.setState({loading:false}); - } - ); + const {fetchCollectionsStartAsync} = this.props; + fetchCollectionsStartAsync(); } render() { const { match } = this.props; - const { loading } = this.state; + return(
- } /> - } /> + +
); } } + const mapDispatchToProps = dispatch => ({ - updateCollections: collectionsMap => dispatch(updateCollections(collectionsMap)) + fetchCollectionsStartAsync: () => dispatch(fetchCollectionsStartAsync()) }) export default connect(null, mapDispatchToProps)(ShopPage); \ No newline at end of file diff --git a/src/redux/shop/shop.actions.js b/src/redux/shop/shop.actions.js index 878115b..737aa1b 100644 --- a/src/redux/shop/shop.actions.js +++ b/src/redux/shop/shop.actions.js @@ -1,6 +1,29 @@ import ShopActionTypes from './shop.type'; +import {firestore, convertCollectionsSnapshotToMap} from '../../fireabase/firebase.utils'; -export const updateCollections = collectionsMap => ({ - type: ShopActionTypes.UPDATE_COLLECTIONS, - payload: collectionsMap -}); \ No newline at end of file +export const fetchCollectionsStart = () => ({ + type: ShopActionTypes.FETCH_COLLECTIONS_START +}); + +export const fetchCollectionsSuccess = collectionMap => ({ + type: ShopActionTypes.FETCH_COLLECTIONS_SUCCESS, + payload: collectionMap +}) +export const fetchCollectionsFailure = errorMessage => ({ + type: ShopActionTypes.FETCH_COLLECTIONS_FAILURE, + payload: errorMessage +}) + +export const fetchCollectionsStartAsync = () => { + return dispatch => { + const collectionRef = firestore.collection('collections'); + dispatch(fetchCollectionsStart()); + + collectionRef.get().then( + snapshot=>{ + const collectionsMap = convertCollectionsSnapshotToMap(snapshot); + dispatch(fetchCollectionsSuccess(collectionsMap)); + } + ).catch(error=> dispatch(fetchCollectionsFailure(error))); + } +} \ No newline at end of file diff --git a/src/redux/shop/shop.reducer.js b/src/redux/shop/shop.reducer.js index 266d989..fbd8d79 100644 --- a/src/redux/shop/shop.reducer.js +++ b/src/redux/shop/shop.reducer.js @@ -1,17 +1,34 @@ import ShopActionTypes from './shop.type'; const INITIAL_STATE = { - collections: null + collections: null, + isFetching: false, + errorMessage: undefined } export const shopReducer = (state=INITIAL_STATE, action) => { switch(action.type){ - case ShopActionTypes.UPDATE_COLLECTIONS: + case ShopActionTypes.FETCH_COLLECTIONS_START: return { ...state, + isFetching: true + }; + + case ShopActionTypes.FETCH_COLLECTIONS_SUCCESS: + return { + ...state, + isFetching: false, collections: action.payload } + case ShopActionTypes.FETCH_COLLECTIONS_FAILURE: + return { + ...state, + isFetching: false, + errorMessage: action.payload + } default: return state; } -}; \ No newline at end of file +}; + +export default shopReducer; \ No newline at end of file diff --git a/src/redux/shop/shop.selector.js b/src/redux/shop/shop.selector.js index b4e00fa..9cedead 100644 --- a/src/redux/shop/shop.selector.js +++ b/src/redux/shop/shop.selector.js @@ -23,4 +23,14 @@ export const selectShopCollection = memoize((collectionUrlParam) => export const selectCollectionForPreview = createSelector( [selectShopCollectionWMemoize], collections=> collections ? Object.keys(collections).map(key=>collections[key]) : [] +) + +export const selectIsCollectionFetching = createSelector ( + [selectShop], + shop => shop.isFetching +) + +export const selectIsCollectionsLoaded = createSelector( + [selectShop], + shop => !! shop.collections ) \ No newline at end of file diff --git a/src/redux/shop/shop.type.js b/src/redux/shop/shop.type.js index 47dee69..dbc59f5 100644 --- a/src/redux/shop/shop.type.js +++ b/src/redux/shop/shop.type.js @@ -1,5 +1,7 @@ const ShopActionTypes = { - UPDATE_COLLECTIONS: 'UPDATE_COLLECTIONS' + FETCH_COLLECTIONS_START: 'FETCH_COLLECTIONS_START', + FETCH_COLLECTIONS_SUCCESS: 'FETCH_COLLECTIONS_SUCCESS', + FETCH_COLLECTIONS_FAILURE: 'FETCH_COLLECTIONS_FAILURE' } export default ShopActionTypes; \ No newline at end of file diff --git a/src/redux/store.js b/src/redux/store.js index 4386144..bc6edfa 100644 --- a/src/redux/store.js +++ b/src/redux/store.js @@ -1,10 +1,11 @@ import {createStore, applyMiddleware} from 'redux'; import logger from 'redux-logger'; -import { persistStore } from 'redux-persist' +import { persistStore } from 'redux-persist'; +import thunk from 'redux-thunk'; import rootReducer from './root-reducer'; -const middlewares = []; +const middlewares = [thunk]; if(process.env.NODE_ENV==='development'){ middlewares.push(logger); diff --git a/yarn.lock b/yarn.lock index 383d41c..1490378 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10706,6 +10706,11 @@ redux-persist@^6.0.0: resolved "https://registry.yarnpkg.com/redux-persist/-/redux-persist-6.0.0.tgz#b4d2972f9859597c130d40d4b146fecdab51b3a8" integrity sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ== +redux-thunk@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622" + integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw== + redux@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f"