diff --git a/app/actions/auction.js b/app/actions/auction.js new file mode 100644 index 0000000..4864494 --- /dev/null +++ b/app/actions/auction.js @@ -0,0 +1,15 @@ +import { + SET_AUCTION_FILTER, + SET_AUCTION_VIEW_MODE, +} from '../constants/actionTypes.js'; + +export const changeFilterMode = (payload) => ({ + type: SET_AUCTION_FILTER, + payload, +}); + +export const changeViewMode = (payload) => ({ + type: SET_AUCTION_VIEW_MODE, + payload, +}); + diff --git a/app/actions/auctionStatus.js b/app/actions/auctionStatus.js new file mode 100644 index 0000000..e939d04 --- /dev/null +++ b/app/actions/auctionStatus.js @@ -0,0 +1,39 @@ +import { List } from 'immutable'; + +import { getEndpointUrl } from '../api/index.js'; + +import { + AUCTIONS_UPDATED, + ITEMS_LOADED, +} from '../constants/actionTypes.js'; + +import { blockUI, unblockUI } from './index.js'; +import { API_ENDPOINTS } from '../constants/constants.js'; + +import Auction from '../domain/Auction.js'; + + +const autionStatusLoadSuccess = (auctions, dispatch) => { + const payload = List(auctions).map((i) => Auction.fromJS(i)); + dispatch({ type: AUCTIONS_UPDATED, payload }); + dispatch(unblockUI); +}; + +export const fetchAuctionStatus = () => (dispatch, getState) => { + const state = getState(); + const activeEvent = state.get('activeEvent'); + + let apiUrl = getEndpointUrl(API_ENDPOINTS.GET_STATUS); + apiUrl = apiUrl.replace(/:event_id$/, ''); + if (activeEvent) { + apiUrl = `${apiUrl}${activeEvent}`; + } + + dispatch(blockUI()); + + return fetch(apiUrl) + .then(response => response.json()) + .then(payload => autionStatusLoadSuccess(payload, dispatch)) + .catch(err => console.error('[actions::getStatus]', err)); +}; + diff --git a/app/actions/events.js b/app/actions/events.js new file mode 100644 index 0000000..4f69670 --- /dev/null +++ b/app/actions/events.js @@ -0,0 +1,28 @@ +import { List } from 'immutable'; + +import { getEndpointUrl } from '../api/index.js'; + +import { + EVENTS_LOADED, + GET_EVENTS, +} from '../constants/actionTypes.js'; + +import { blockUI, unblockUI } from './index.js'; +import { API_ENDPOINTS } from '../constants/constants.js'; + +import Event from '../domain/Event.js'; + + +const eventsLoadSuccess = (events, dispatch) => { + const payload = List(events).map((i) => Event.fromJS(i)); + dispatch({ type: EVENTS_LOADED, payload }); + dispatch(unblockUI); +}; + +export const fetchEvents = () => (dispatch) => { + dispatch(blockUI()); + fetch(getEndpointUr(API_ENDPOINTS.GET_EVENTS)) + .then(response => response.json()) + .then(payload => eventsLoadSuccess(payload, dispatch)) + .catch(err => console.error('[actions::getEvents]', err)); +}; diff --git a/app/actions/index.js b/app/actions/index.js index 716381c..2b7aac3 100644 --- a/app/actions/index.js +++ b/app/actions/index.js @@ -1,78 +1,8 @@ -import { List } from 'immutable'; - -import { getEndpointUrl } from '../api/index.js'; - -import Auction from '../domain/Auction.js'; -import Event from '../domain/Event.js'; -import Item from '../domain/Item.js'; - import { - AUCTIONS_UPDATED, BLOCK_UI, - EVENTS_LOADED, - GET_EVENTS, - GET_ITEMS, - ITEMS_LOADED, UNBLOCK_UI, } from '../constants/actionTypes.js'; -import { API_ENDPOINTS } from '../constants/constants.js'; - -export const getEvents = () => (dispatch) => { - dispatch(blockUI()); - fetch(getEndpointUr(API_ENDPOINTS.GET_EVENTS)) - .then(response => response.json()) - .then((payload) => { - const events = List(payload).map((i) => Event.fromJS(i)); - dispatch({ type: EVENTS_LOADED, payload: events }); - dispatch(unblockUI); - }); -}; - -export const getItems = () => (dispatch, getState) => { - const state = getState(); - const activeEvent = state.get('activeEvent'); - - let apiUrl = getEndpointUrl(API_ENDPOINTS.GET_ITEMS); - apiUrl = apiUrl.replace(/:event_id$/, ''); - if (activeEvent) { - apiUrl = `${apiUrl}${activeEvent}`; - } - - dispatch(blockUI()); - - fetch(apiUrl) - .then(response => response.json()) - .then(payload => { - const items = List(payload).map(i => Item.fromJS(i)); - dispatch({ type: ITEMS_LOADED, payload: items }); - dispatch(unblockUI()); - }) - .catch(err => console.error('[actions::getItems]', err)); -}; - -export const getStatus = () => (dispatch, getState) => { - const state = getState(); - const activeEvent = state.get('activeEvent'); - - let apiUrl = getEndpointUrl(API_ENDPOINTS.GET_STATUS); - apiUrl = apiUrl.replace(/:event_id$/, ''); - if (activeEvent) { - apiUrl = `${apiUrl}${activeEvent}`; - } - - dispatch(blockUI()); - - return fetch(apiUrl) - .then(response => response.json()) - .then(payload => { - const auctions = List(payload).map(i => Auction.fromJS(i)); - dispatch(unblockUI()); - dispatch({ type: AUCTIONS_UPDATED, payload: auctions }); - }) - .catch(err => console.error('[actions::getStatus]', err)); -}; - export const blockUI = () => ({ type: BLOCK_UI, }); diff --git a/app/actions/items.js b/app/actions/items.js new file mode 100644 index 0000000..01c9ccd --- /dev/null +++ b/app/actions/items.js @@ -0,0 +1,38 @@ +import { List } from 'immutable'; + +import { getEndpointUrl } from '../api/index.js'; + +import { + GET_ITEMS, + ITEMS_LOADED, +} from '../constants/actionTypes.js'; + +import { blockUI, unblockUI } from './index.js'; +import { API_ENDPOINTS } from '../constants/constants.js'; + +import Item from '../domain/Item.js'; + + +const itemsLoadSuccess = (items, dispatch) => { + const payload = List(items).map((i) => Item.fromJS(i)); + dispatch({ type: ITEMS_LOADED, payload }); + dispatch(unblockUI); +}; + +export const fetchItems = () => (dispatch, getState) => { + const state = getState(); + const activeEvent = state.get('activeEvent'); + + let apiUrl = getEndpointUrl(API_ENDPOINTS.GET_ITEMS); + apiUrl = apiUrl.replace(/:event_id$/, ''); + if (activeEvent) { + apiUrl = `${apiUrl}${activeEvent}`; + } + + dispatch(blockUI()); + + fetch(apiUrl) + .then(response => response.json()) + .then(payload => itemsLoadSuccess(payload, dispatch)) + .catch(err => console.error('[actions::getItems]', err)); +}; diff --git a/app/constants/actionTypes.js b/app/constants/actionTypes.js index bb1958e..a74b001 100644 --- a/app/constants/actionTypes.js +++ b/app/constants/actionTypes.js @@ -49,3 +49,9 @@ export const UNBLOCK_UI = 'UNBLOCK_UI'; export const SET_ACTIVE_EVENT = 'SET_ACTIVE_EVENT'; export const UNSET_ACTIVE_EVENT = 'UNSET_ACTIVE_EVENT'; + +export const SET_ACTIVE_ITEM = 'SET_ACTIVE_ITEM'; +export const UNSET_ACTIVE_ITEM = 'UNSET_ACTIVE_ITEM'; + +export const SET_AUCTION_FILTER = 'SET_AUCTION_FILTER'; +export const SET_AUCTION_VIEW_MODE = 'SET_AUCTION_VIEW_MODE'; diff --git a/app/constants/constants.js b/app/constants/constants.js index 9ff806b..271a04d 100644 --- a/app/constants/constants.js +++ b/app/constants/constants.js @@ -1,3 +1,7 @@ +export const ITEM_FILTERS = { + ALL: 'ALL', +}; + export const ITEM_TYPES = { AUCTION: 'auction', DONATION: 'donation', @@ -19,9 +23,11 @@ export const SORT_MODES = { TITLE_DSC: 'TITLE_DSC', }; -export const VIEW_MODES = { - LIST: 'LIST', - GRID: 'GRID', +export const AUCTION_VIEW_MODES = { + ALL: 'ALL', + BIDDING: 'BIDDING', + NO_BIDS: 'NO_BIDS', + WINNING: 'WINNING', }; export const API_ENDPOINTS = { diff --git a/app/containers/Auction.js b/app/containers/Auction.js index 368f407..3e58c36 100644 --- a/app/containers/Auction.js +++ b/app/containers/Auction.js @@ -1,18 +1,23 @@ import { connect } from 'react-redux'; -import { getItems, getStatus } from '../actions/index.js'; +import { fetchItems } from '../actions/items.js'; +import { fetchAuctionStatus } from '../actions/auctionStatus.js'; import { getAuctionItemsAsList } from '../selectors/items.js'; import Auction from '../screens/Auction.js'; -const matchStateToProps = (state) => ({ - items: getAuctionItemsAsList(state), -}); +const matchStateToProps = (state) => { + const items = getAuctionItemsAsList(state); + console.log('items:', items); + + return { items }; +}; const mapDispatchToProps = (dispatch) => ({ - fetchItems: () => dispatch(getItems(dispatch)), - fetchStatus: () => dispatch(getStatus(dispatch)), + changeViewMode: (mode) => dispatch(changeViewMode(mode)), + fetchItems: () => dispatch(fetchItems(dispatch)), + fetchStatus: () => dispatch(fetchAuctionStatus(dispatch)), }); export default connect(matchStateToProps, mapDispatchToProps)(Auction); diff --git a/app/containers/Event.js b/app/containers/Event.js new file mode 100644 index 0000000..6716cc8 --- /dev/null +++ b/app/containers/Event.js @@ -0,0 +1,34 @@ +import { connect } from 'react-redux'; + +import { fetchEvent } from '../actions/events.js'; +import { getEventById } from '../selectors/events.js'; + +import Event from '../screens/Event.js'; + +const matchStateToProps = (state) => { + const eventId = state.get('activeEvent'); + const event = getEventById(state, eventId); + + return { + description: event.get('description'), + endTime: event.get('endTime'), + id: event.get('id'), + images: event.get('images'), + isTicketed: event.get('isTicketed'), + posts: event.get('posts'), + requireLoginToSeeAuction: event.get('requireLoginToSeeAuction'), + showFrom: event.get('showFrom'), + showUntil: event.get('showUntil'), + startTime: event.get('startTime'), + tagline: event.get('tagline'), + ticketClasses: event.get('ticketClasses'), + title: event.get('title'), + url: event.get('url'), + }; +}; + +const mapDispatchToProps = (dispatch) => ({ + fetchEvent: () => dispatch(fetchEvent(dispatch)), +}); + +export default connect(matchStateToProps, mapDispatchToProps)(Event); diff --git a/app/containers/Events.js b/app/containers/Events.js new file mode 100644 index 0000000..f5124d4 --- /dev/null +++ b/app/containers/Events.js @@ -0,0 +1,19 @@ +import { connect } from 'react-redux'; + +import { fetchEvents } from '../actions/events.js'; +import { getEventsAsList } from '../selectors/events.js'; + +import Events from '../screens/Events.js'; + +const matchStateToProps = (state) => { + const events = getEventsAsList(state); + console.log('events:', events); + + return { events }; +}; + +const mapDispatchToProps = (dispatch) => ({ + fetchEvents: () => dispatch(fetchEvents(dispatch)), +}); + +export default connect(matchStateToProps, mapDispatchToProps)(Events); diff --git a/app/containers/Item/Grid.js b/app/containers/Item/Grid.js new file mode 100644 index 0000000..e69de29 diff --git a/app/containers/Item/Item.js b/app/containers/Item/Item.js new file mode 100644 index 0000000..c03d330 --- /dev/null +++ b/app/containers/Item/Item.js @@ -0,0 +1,16 @@ +export const matchStateToProps = (state, ownProps) => { + const { item } = ownProps; + + return { + description: item.get('description'), + donor: item.get('donor'), + end: item.get('end'), + id: item.get('id'), + images: item.get('images').toArray(), + start: item.get('start'), + startingPrice: item.get('startingPrice'), + subtitle: item.get('subtitle'), + title: item.get('title'), + type: item.get('type'), + }; +}; diff --git a/app/containers/Item/List.js b/app/containers/Item/List.js new file mode 100644 index 0000000..57e009d --- /dev/null +++ b/app/containers/Item/List.js @@ -0,0 +1,6 @@ +import { connect } from 'react-redux'; + +import { mapStateToProps } from './Item.js'; +import ItemRow from '../../components/Item/List.js'; + +export default connect(mapStateToProps, null)(ItemRow); diff --git a/app/reducers/activeItem.js b/app/reducers/activeItem.js new file mode 100644 index 0000000..27d92ed --- /dev/null +++ b/app/reducers/activeItem.js @@ -0,0 +1,12 @@ +import { SET_ACTIVE_ITEM, UNSET_ACTIVE_ITEM } from '../constants/actionTypes.js'; + +export const activeItem = (state = null, action) => { + switch (action.type) { + case SET_ACTIVE_ITEM: + return action.payload; + case UNSET_ACTIVE_ITEM: + return null; + default: + return state; + } +}; diff --git a/app/reducers/auctionFilter.js b/app/reducers/auctionFilter.js new file mode 100644 index 0000000..cfb90d4 --- /dev/null +++ b/app/reducers/auctionFilter.js @@ -0,0 +1,11 @@ +import { SET_ITEM_FILTER } from '../constants/actionTypes.js'; +import { ITEM_FILTERS } from '../constants/constants.js'; + +export const itemFilter = (state = ITEM_FILTERS.ALL, action) => { + switch (action.type) { + case SET_AUCTION_FILTER: + return action.payload; + default: + return state; + } +}; diff --git a/app/reducers/auctionView.js b/app/reducers/auctionView.js new file mode 100644 index 0000000..87777d8 --- /dev/null +++ b/app/reducers/auctionView.js @@ -0,0 +1,11 @@ +import { SET_AUCTION_VIEW_MODE } from '../constants/actionTypes.js'; +import { AUCTION_VIEW_MODES } from '../constants/constants.js'; + +export const auctionView = (state = AUCTION_VIEW_MODES.ALL, action) => { + switch (action.type) { + case SET_AUCTION_VIEW_MODE: + return action.payload; + default: + return state; + } +}; diff --git a/app/reducers/index.js b/app/reducers/index.js index 6d4a27f..58e24ee 100644 --- a/app/reducers/index.js +++ b/app/reducers/index.js @@ -1,14 +1,20 @@ import { combineReducers } from 'redux-immutable'; import { activeEvent } from './activeEvent.js'; +import { activeItem } from './activeItem.js'; +import { auctionFilter } from './auctionFilter.js'; import { auctions } from './auctions.js'; +import { auctionView } from './auctionView.js'; import { blockUI } from './blockUI.js'; import { events } from './events.js'; import { items } from './items.js'; const rootReducer = combineReducers({ activeEvent, + activeItem, + auctionFilter, auctions, + auctionView, blockUI, events, items, diff --git a/app/screens/Auction.js b/app/screens/Auction.js index 8c00674..2afe008 100644 --- a/app/screens/Auction.js +++ b/app/screens/Auction.js @@ -1,3 +1,4 @@ +import { List } from 'immutable'; import React, { Component } from 'react'; import PropTypes from 'prop-types'; @@ -8,11 +9,10 @@ import { View, } from 'react-native'; -import { SORT_MODES, VIEW_MODES } from '../constants/constants.js'; +import { SORT_MODES, AUCTION_VIEW_MODES } from '../constants/constants.js'; import FilterBar from '../components/Auction/FilterBar.js'; -import GridItem from '../components/Item/Grid.js'; -import ListItem from '../components/Item/List.js'; +import ListItem from '../containers/Item/List.js'; export default class Auction extends Component { static get propTypes() { @@ -20,7 +20,10 @@ export default class Auction extends Component { changeFilter: PropTypes.func, fetchItems: PropTypes.func.isRequired, fetchStatus: PropTypes.func.isRequired, - items: PropTypes.array, + items: PropTypes.oneOfType([ + PropTypes.array, + PropTypes.instanceOf(List), + ]), }; } @@ -39,7 +42,7 @@ export default class Auction extends Component { this.state = { sort: SORT_MODES.TITLE_ASC, - view: VIEW_MODES.LIST, + view: AUCTION_VIEW_MODES.ALL, }; } @@ -58,14 +61,7 @@ export default class Auction extends Component { _keyExtractor = (item, index) => `${item._id}_${index}`; - _renderItem = (view) => ({ item }) => { - console.log('_renderItem', item); - if (view === VIEW_MODES.GRID) { - return ; - } - - return ; - } + _renderItem = ({ item }) => ; render() { const { items } = this.props; diff --git a/app/selectors/auctions.js b/app/selectors/auctions.js index cb90519..81d7ea7 100644 --- a/app/selectors/auctions.js +++ b/app/selectors/auctions.js @@ -1,7 +1,5 @@ import { createSelector } from 'reselect'; -//import { getItemsIdsWithNoBids, getMyBidItemIds, getMyWinningItemIds } from './auctions.js'; - const getState = (state) => state; export const getItemBidCount = (state, itemId) => state.getIn(['auctions', itemId, 'bidCount'], 0); @@ -11,3 +9,25 @@ export const getItemPrice = (state, itemId) => state.getIn(['auctions', itemId, export const isBiddingItem = (state, itemId) => state.getIn(['auctions', itemId, 'isBidding'], false); export const isWinningItem = (state, itemId) => state.getIn(['auctions', itemId, 'isWinning'], false); + +export const getAuctionStatus = (state, itemId) => state.getIn(['actions', itemId], false); + +export const getAuctionStatuses = createSelector( + [getState], + (state) => state.get('actions') || new Map(), +); + +export const getItemsIdsWithNoBids = createSelector( + [getAuctionStatuses], + (auctions) => auctions.filter(auction => auction.bidCount === 0); +); + +export const getMyBidItemIds = createSelector( + [getAuctionStatuses], + (auctions) => auctions.filter(auction => auction.isBidding); +); + +export const getMyWinningItemIds = createSelector( + [getAuctionStatuses], + (auctions) => auctions.filter(auction => auction.isWinning); +); diff --git a/app/selectors/events.js b/app/selectors/events.js new file mode 100644 index 0000000..26c7560 --- /dev/null +++ b/app/selectors/events.js @@ -0,0 +1,21 @@ +import { Map } from 'immutable'; +import { createSelector } from 'reselect'; + +const getState = (state) => state; + +export const getActiveEvent = (state) => { + const eventId = state.get('activeEvent'); + return state.getIn(['events', eventId], false) +}; + +export const getEventById = (state, eventId) => state.getIn(['events', eventId], false); + +export const getEvents = createSelector( + [getState], + (state) => state.get('events') || new Map(), +); + +export const getEventsAsList = createSelector( + [getEvents], + (eventsAsMap) => eventsAsMap.toList(), +); diff --git a/app/selectors/items.js b/app/selectors/items.js index 340cad3..29bba08 100644 --- a/app/selectors/items.js +++ b/app/selectors/items.js @@ -1,6 +1,7 @@ +import { Map } from 'immutable'; import { createSelector } from 'reselect'; -//import { getItemsIdsWithNoBids, getMyBidItemIds, getMyWinningItemIds } from './auctions.js'; +import { getItemsIdsWithNoBids, getMyBidItemIds, getMyWinningItemIds } from './auctions.js'; const getState = (state) => state; @@ -26,6 +27,36 @@ export const getAuctionItemsAsList = createSelector( (auctionItemsAsMap) => auctionItemsAsMap.toList(), ); +export const getAuctionItemsUserIsBidding = createSelector( + [getAuctionItems, getMyBidItemIds], + (items, myBids) => items.filter(i => myBids.indexOf(i.id)) || new Map(), +); + +export const getAuctionItemsUserIsBiddingAsList = createSelector( + [getAuctionItemsUserIsBidding], + (auctionItemsAsMap) => auctionItemsAsMap.toList(), +); + +export const getAuctionItemsUserIsWinning = createSelector( + [getAuctionItemsUserIsBidding, getMyWinningItemIds], + (items, myWins) => items.filter(i => myWins.indexOf(i.id)) || new Map(), +); + +export const getAuctionItemsUserIsWinningAsList = createSelector( + [getAuctionItemsUserIsWinning], + (auctionItemsAsMap) => auctionItemsAsMap.toList(), +); + +export const getAuctionItemsWithNoBids = createSelector( + [getAuctionItems, getItemsIdsWithNoBids], + (items, noBids) => items.filter(i => noBids.indexOf(i.id)) || new Map(), +); + +export const getAuctionItemsWithNoBidsAsList = createSelector( + [getAuctionItemsWithNoBids], + (auctionItemsAsMap) => auctionItemsAsMap.toList(), +); + export const getTicketItems = createSelector( [getState], (state) => state.get('items').filter(i => i.type === 'ticket') || new Map(), @@ -35,8 +66,3 @@ export const getTicketItemsAsList = createSelector( [getTicketItems], (ticketItemsAsMap) => ticketItemsAsMap.toList(), ); - -export const getAuctionItemsWithNoBids = createSelector( - [getAuctionItems], - (auctionItemsAsMap) => auctionItemsAsMap.filter(i => i.bidCount), -);