This commit is contained in:
2019-07-24 00:53:01 -04:00
parent 434a1ded24
commit a9f4324f29
21 changed files with 345 additions and 100 deletions

15
app/actions/auction.js Normal file
View File

@@ -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,
});

View File

@@ -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));
};

28
app/actions/events.js Normal file
View File

@@ -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));
};

View File

@@ -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,
});

38
app/actions/items.js Normal file
View File

@@ -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));
};

View File

@@ -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';

View File

@@ -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 = {

View File

@@ -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);

34
app/containers/Event.js Normal file
View File

@@ -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);

19
app/containers/Events.js Normal file
View File

@@ -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);

View File

View File

@@ -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'),
};
};

View File

@@ -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);

View File

@@ -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;
}
};

View File

@@ -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;
}
};

View File

@@ -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;
}
};

View File

@@ -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,

View File

@@ -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 <GridItem {...item} />;
}
return <ListItem {...item} />;
}
_renderItem = ({ item }) => <ListItem item={item} />;
render() {
const { items } = this.props;

View File

@@ -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);
);

21
app/selectors/events.js Normal file
View File

@@ -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(),
);

View File

@@ -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),
);