- Cleanup

This commit is contained in:
2019-08-06 01:36:08 -04:00
parent 6e71ac688a
commit b8ddc54b99
43 changed files with 361 additions and 170 deletions

View File

@@ -6,7 +6,7 @@
* @flow * @flow
*/ */
import React, {Fragment} from 'react'; import React from 'react';
import { createAppContainer } from 'react-navigation'; import { createAppContainer } from 'react-navigation';
import { Tabs } from './router.js'; import { Tabs } from './router.js';

View File

@@ -1,33 +1,36 @@
import { List } from 'immutable'; import { List } from 'immutable';
import { getEndpointUrl } from '../api/index.js'; import { blockUI, unblockUI } from './index.js';
import { fetchEvents as fetchEventsApi } from '../api/events.js';
import { import {
EVENTS_LOAD_FAILED,
EVENTS_LOADED, EVENTS_LOADED,
GET_EVENTS, GET_EVENTS,
} from '../constants/actionTypes.js'; } from '../constants/actionTypes.js';
import { blockUI, unblockUI } from './index.js';
import { API_ENDPOINTS } from '../constants/constants.js';
import Event from '../domain/Event.js'; import Event from '../domain/Event.js';
import { getAuthToken } from '../selectors/auth.js';
const eventsLoaded = (payload) => ({ type: EVENTS_LOADED, payload });
const eventsLoadSuccess = (events, dispatch) => { const eventsLoadError = (payload) => ({ type: EVENTS_LOAD_FAILED, payload });
const payload = List(events).map((i) => Event.fromJS(i));
dispatch({ type: EVENTS_LOADED, payload }); const eventsFetchSuccess = (items) => (dispatch) => {
const payload = List(items).map((i) => Event.fromJS(i));
dispatch(eventsLoaded(payload));
dispatch(unblockUI); dispatch(unblockUI);
}; };
export const setActiveEvent = (eventId) => ({ const eventsFetchFailure = (error) => (dispatch) => {
type: SET_ACTIVE_EVENT, console.error('[actions::events::eventsFetchFailure]', error);
payload: eventId, dispatch(eventsLoadError(error));
}); dispatch(unblockUI);
};
export const fetchEvents = () => (dispatch) => {
dispatch(blockUI()); export const fetchEvents = () => (dispatch, getState) => {
fetch(getEndpointUr(API_ENDPOINTS.GET_EVENTS)) const authToken = getAuthToken(getState());
.then(response => response.json())
.then(payload => eventsLoadSuccess(payload, dispatch)) fetchEventsApi(authToken)
.catch(err => console.error('[actions::getEvents]', err)); .then(payload => dispatch(eventsFetchSuccess(payload)))
.catch(err => dispatch(eventsFetchFailure(err)));
}; };

View File

@@ -1,12 +1,12 @@
import { List } from 'immutable'; import { List } from 'immutable';
import { fetchItems } from '../api/items.js'; import { fetchItems as fetchItemsApi } from '../api/items.js';
import { import {
GET_ITEMS, GET_ITEMS,
ITEMS_LOADED, ITEMS_LOADED,
} from '../constants/actionTypes.js'; } from '../constants/actionTypes.js';
import { getActiveEventId } from '../selectors/activeEvent.js'; import { getActiveEventId } from '../selectors/activeEvent.js';
import { getLoginToken } from '../selectors/profile.js'; import { getAuthToken } from '../selectors/auth.js';
import { blockUI, unblockUI } from './index.js'; import { blockUI, unblockUI } from './index.js';
@@ -24,16 +24,16 @@ const itemsFetchSuccess = (items) => (dispatch) => {
}; };
const itemsFetchFailure = (error) => (dispatch) => { const itemsFetchFailure = (error) => (dispatch) => {
console.error('[actions::getItems]', error)); console.error('[actions::items::itemsFetchFailure]', error);
dispatch(itemsLoadFailure(error)); dispatch(itemsLoadFailure(error));
dispatch(unblockUI); dispatch(unblockUI);
}; };
export const fetchItems = () => (dispatch, getState) => { export const fetchItems = () => (dispatch, getState) => {
const eventId = getActiveEventId(getState()); const eventId = getActiveEventId(getState());
const authToken = getLoginToken(getState()); const authToken = getAuthToken(getState());
fetchItems(activeEvent, authToken) fetchItemsApi(activeEvent, authToken)
.then(payload => dispatch(itemsFetchSuccess(payload))) .then(payload => dispatch(itemsFetchSuccess(payload)))
.catch(err => dispatch(itemsFetchFailure(err)); .catch(err => dispatch(itemsFetchFailure(err)));
}; };

View File

@@ -3,13 +3,18 @@ import { blockUI, unblockUI } from './index.js';
import { import {
getEmailAvailability, getEmailAvailability,
getNomAvailaibility, getNomAvailaibility,
loginUser,
registerNewUser, registerNewUser,
} from '../api/profile.js'; } from '../api/profile.js';
import { import {
DO_LOGOUT, DO_LOGOUT,
LOGIN_FAILURE,
LOGIN_SUCCESS,
PROFILE_EMAIL_AVAILABLE, PROFILE_EMAIL_AVAILABLE,
PROFILE_NOM_AVAILABLE, PROFILE_NOM_AVAILABLE,
UNSET_AUTH,
UNSET_PROFILE,
UPDATE_PROFILE, UPDATE_PROFILE,
} from '../constants/actionTypes.js'; } from '../constants/actionTypes.js';
@@ -23,6 +28,16 @@ const isValidNom = (payload) => ({
payload, payload,
}); });
const loginFailure = (payload) => ({
type: LOGIN_FAILURE,
payload,
});
const loginSuccess = (payload) => ({
type: LOGIN_SUCCESS,
payload,
});
const logoutUser = () => ({ const logoutUser = () => ({
type: DO_LOGOUT, type: DO_LOGOUT,
}); });
@@ -37,6 +52,14 @@ const registrationSuccess = (payload) => ({
payload, payload,
}); });
const unsetAuth = () => ({
type: UNSET_AUTH,
});
const unsetProfile = () => ({
type: UNSET_PROFILE,
});
const updateProfile = (profile) => ({ const updateProfile = (profile) => ({
type: UPDATE_PROFILE, type: UPDATE_PROFILE,
payload: profile, payload: profile,
@@ -50,8 +73,20 @@ export const checkNomAvailability = (nomDeBid) => (dispatch) => {
}; };
export const login = (username, password) => (dispatch) => {
dispatch(blockUI());
loginUser(username, password)
.then(result => {
dispatch(loginSuccess(result))
})
.catch(err => dispatch(loginFailure(err)));
};
export const logout = () => (dispatch) => dispatch(logoutUser()); export const logout = () => (dispatch) => {
dispatch(unsetProfile());
dispatch(unsetAuth());
dispatch(logoutUser());
};
// USER REGISTRATION // USER REGISTRATION
const handleRegistrationSuccess = (user) => (dispatch) => { const handleRegistrationSuccess = (user) => (dispatch) => {

7
app/api/events.js Normal file
View File

@@ -0,0 +1,7 @@
import { API_ENDPOINTS, requestGet } from './index.js';
export const fetchEvents = (auth) => {
const path = String(API_ENDPOINTS.GET_EVENTS);
const opts = { Authorization: auth ? `Bearer ${auth}` : null };
return requestGet(path, null, opts);
};

View File

@@ -40,7 +40,7 @@ const parseQueryParamsString = (queryParams) => {
}; };
const parseQueryParamsObject = (queryParams) => { const parseQueryParamsObject = (queryParams) => {
if (typeof queryParams !== 'object' && Array.isArray(queryParams)) { if (queryParams === null || typeof queryParams !== 'object' || Array.isArray(queryParams)) {
return null; return null;
} }

View File

@@ -11,7 +11,7 @@ import { API_URL } from '../constants/constants.js';
const DefaultRequestOptions = {}; const DefaultRequestOptions = {};
const endpoints = { export const API_ENDPOINTS = {
// Events and Items // Events and Items
GET_EVENTS: '/events', GET_EVENTS: '/events',
// GET_ITEMS: '/items?eventId=:event_id', // GET_ITEMS: '/items?eventId=:event_id',
@@ -23,6 +23,9 @@ const endpoints = {
PLACE_BID: '/bids/:item_id', PLACE_BID: '/bids/:item_id',
PURCHASE_ITEM: '/sales', PURCHASE_ITEM: '/sales',
// Login
LOGIN: '/auth',
// User/Profile // User/Profile
USER_SIGNUP: '/signup', USER_SIGNUP: '/signup',
USER_PROFILE: '/users/:user_id', USER_PROFILE: '/users/:user_id',
@@ -42,11 +45,11 @@ const cacheBuster = () => {
}; };
export const getEndpointUrl = (endpoint) => { export const getEndpointUrl = (endpoint) => {
if (!endpoints[endpoint]) { if (!API_ENDPOINTS[endpoint]) {
throw new Error('Invalid API endpoint specified'); throw new Error('Invalid API endpoint specified');
} }
return `${API_URL}${endpoints[endpoint]}`; //`${cacheBuster()}`; return `${API_URL}${API_ENDPOINTS[endpoint]}`; //`${cacheBuster()}`;
}; };
export const requestGet = (path, queryParams = [], requestOptions = {}) => { export const requestGet = (path, queryParams = [], requestOptions = {}) => {

View File

@@ -1,4 +1,4 @@
import { getEndpointUrl, requestGet } from './index.js'; import { API_ENDPOINTS, requestGet } from './index.js';
export const fetchItems = (eventId, auth) => { export const fetchItems = (eventId, auth) => {
const path = String(API_ENDPOINTS.GET_ITEMS).replace(/:event_id$/, eventId); const path = String(API_ENDPOINTS.GET_ITEMS).replace(/:event_id$/, eventId);

View File

@@ -0,0 +1,18 @@
import { API_ENDPOINTS, requestGet } from './index.js';
export const getEmailAvailability = (email) => {
};
export const getNomAvailaibility = (nomDeBid) => {
};
export const loginUser = (username, password) => {
const path = String(API_ENDPOINTS.LOGIN);
return requestPost({
path: API_ENDPOINTS.LOGIN,
body: { username, password },
});
};
export const registerNewUser = (user) => {
};

View File

@@ -3,20 +3,22 @@ import PropTypes from 'prop-types';
import { Header } from 'react-native-elements'; import { Header } from 'react-native-elements';
import HeaderTitle from './HeaderTitle.container.js'; import HeaderTitle from './HeaderTitle/HeaderTitle.container.js';
import HeaderContentLeft from './HeaderContentLeft.container.js'; import HeaderContentLeft from './HeaderContentLeft.container.js';
import HeaderContentRight from './HeaderContentRight.container.js'; import HeaderContentRight from './HeaderContentRight.container.js';
import styles from './AppHeader.styles.js'; import styles from './AppHeader.styles.js';
export default function AppHeader({ navigation }) ( export default function AppHeader({ navigation }) {
<Header return (
placement="left" <Header
leftComponent={<HeaderContentRight navigation={navigation} />} placement="left"
centerComponent={<HeaderTitle navigation={navigation} />} leftComponent={<HeaderContentRight navigation={navigation} />}
rightComponent={<HeaderContentLeft navigation={navigation} />} centerComponent={<HeaderTitle navigation={navigation} />}
/> rightComponent={<HeaderContentLeft navigation={navigation} />}
) />
);
}
AppHeader.propTypes = { AppHeader.propTypes = {
navigation: PropTypes.func.isRequired, navigation: PropTypes.func.isRequired,

View File

@@ -1,6 +1,6 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { hasMultipleEvents } from '../selectors/events.js'; import { hasMultipleEvents } from '../../selectors/events.js';
import HeaderContentLeft from './HeaderContentLeft.js'; import HeaderContentLeft from './HeaderContentLeft.js';

View File

@@ -1,6 +1,6 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { getProfileAvatarUrl } from '../selectors/profile.js'; import { getProfileAvatarUrl } from '../../selectors/profile.js';
import HeaderContentRight from './HeaderContentRight.js'; import HeaderContentRight from './HeaderContentRight.js';

View File

@@ -1,6 +1,6 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { getActiveEvent, getDefaultEvent } from '../selectors/events.js'; import { getActiveEvent, getDefaultEvent } from '../../../../selectors/events.js';
import EventTitle from './EventTitle.js'; import EventTitle from './EventTitle.js';

View File

@@ -1,6 +1,6 @@
import { StyleSheet } from 'react-native'; import { StyleSheet } from 'react-native';
export default const styles = StyleSheet.create({ export const styles = StyleSheet.create({
eventInfo: { eventInfo: {
flexDirection: 'row', flexDirection: 'row',
}, },
@@ -12,4 +12,3 @@ export default const styles = StyleSheet.create({
flex: 1, flex: 1,
}, },
}); });

View File

@@ -1,7 +1,7 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { hasActiveEvent } from '../selectors/activeEvent.js'; import { hasActiveEvent } from '../../../selectors/activeEvent.js';
import { hasMultipleEvents } from '../selectors/events.js'; import { hasMultipleEvents } from '../../../selectors/events.js';
import HeaderTitle from './HeaderTitle.js'; import HeaderTitle from './HeaderTitle.js';

View File

@@ -9,7 +9,7 @@ import {
import EventTitle from './EventTitle/EventTitle.container.js'; import EventTitle from './EventTitle/EventTitle.container.js';
import styles from './TitleBar.styles.js'; import styles from './HeaderTitle.styles.js';
export default function HeaderTitle({ export default function HeaderTitle({
activeRoute, activeRoute,

View File

@@ -1,6 +1,6 @@
import { StyleSheet } from 'react-native'; import { StyleSheet } from 'react-native';
export default const styles = StyleSheet.create({ export const styles = StyleSheet.create({
filterBar: { filterBar: {
backgroundColor: '#0F0', backgroundColor: '#0F0',
flexDirection: 'row', flexDirection: 'row',

View File

@@ -4,11 +4,13 @@ import PropTypes from 'prop-types';
import { TouchableOpacity } from 'react-native'; import { TouchableOpacity } from 'react-native';
import { Icon } from 'react-native-elements'; import { Icon } from 'react-native-elements';
export default function BackIcon({ action }) ( export default function BackIcon({ action }) {
<TouchableOpacity onPress={action}> return (
<Icon name="ei-chevron-left" type="evilicons" size={28} />; <TouchableOpacity onPress={action}>
</TouchableOpacity> <Icon name="ei-chevron-left" type="evilicons" size={28} />;
) </TouchableOpacity>
);
}
BackIcon.propTypes = { BackIcon.propTypes = {
action: PropTypes.func.isRequired, action: PropTypes.func.isRequired,

View File

@@ -1,11 +1,11 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { getProfileAvatarUrl } from '../selectors/profile.js'; import { getProfileAvatarUrl } from '../../../selectors/profile.js';
import HeaderContentRight from './HeaderContentRight.js'; import UserProfileButton from './UserProfileButton.js';
const matchStateToProps = (state) => ({ const matchStateToProps = (state) => ({
avatarUrl: getProfileAvatarUrl(state), avatarUrl: getProfileAvatarUrl(state),
}); });
export default connect(matchStateToProps, null)(HeaderContentRight); export default connect(matchStateToProps, null)(UserProfileButton);

View File

@@ -20,16 +20,16 @@ export default function UserProfileButton({ avatarUrl, navigation }) {
<Image source={{ uri: avatarUrl }} /> <Image source={{ uri: avatarUrl }} />
</View> </View>
) : ( ) : (
<Icon name="ei-user" type="evilicons" size={28} />; <Icon name="ei-user" type="evilicons" size={28} />
)} )}
</TouchableOpacity> </TouchableOpacity>
); );
} }
HeaderContentRight.propTypes = { UserProfileButton.propTypes = {
avatarUrl: PropTypes.string, avatarUrl: PropTypes.string,
}; };
HeaderContentRight.propTypes = { UserProfileButton.propTypes = {
avatarUrl: null, avatarUrl: null,
}; };

View File

@@ -5,7 +5,7 @@ import {
logout, logout,
registrationServiceError, registrationServiceError,
userCanceledRegistration, userCanceledRegistration,
} from '../actions/profile.js'; } from '../../actions/profile.js';
import FacebookLogin from './FacebookLogin.js'; import FacebookLogin from './FacebookLogin.js';

View File

@@ -0,0 +1,11 @@
import { connect } from 'react-redux';
import { login } from '../../actions/profile.js';
import LocalLogin from './LocalLogin.js';
const mapDispatchToProps = (dispatch) => ({
doLoginAction: (username, password) => dispatch(login(username, password)),
});
export default connect(null, mapDispatchToProps)(LocalLogin);

View File

@@ -0,0 +1,55 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { Button, TextInput, View } from 'react-native';
export default function LocalLogin({ doLoginAction }) {
const [ enabled, setEnableSubmit ] = useState(false);
const [ password, setPassword ] = useState(null);
const [ username, setUsername ] = useState(null);
const _handleLoginSubmit = () => {
doLoginAction(username, password);
};
const _updateState = (field, value) => {
if (field === 'username') {
setUsername(value);
}
if (field === 'password') {
setPassword(value);
}
if (!!username && !!password) {
setEnableSubmit(true);
}
};
return (
<View style={styles.loginWrap}>
<TextInput
style={{height: 40}}
placeholder="email"
onChangeText={(text) => _updateState('username', text)}
value={username}
/>
<TextInput
style={{height: 40}}
placeholder="password"
onChangeText={(text) => _updateState('password', text)}
value={password}
/>
<Button
disabled={!enabled}
onPress={_handleLoginSubmit}
title="Login"
/>
</View>
);
}
LocalLogin.propTypes = {
doLoginAction: PropTypes.func.isRequired,
};

View File

@@ -16,6 +16,12 @@ export const BID_FAILURE = 'BID_FAILURE';
export const BID_SUCCESS = 'BID_SUCCESS'; export const BID_SUCCESS = 'BID_SUCCESS';
export const DO_LOGIN = 'DO_LOGIN'; export const DO_LOGIN = 'DO_LOGIN';
export const LOGIN_FAILURE = 'LOGIN_FAILURE';
export const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
export const SET_AUTH = 'SET_AUTH';
export const UNSET_AUTH = 'UNSET_AUTH';
export const DO_LOGOUT = 'DO_LOOUT'; export const DO_LOGOUT = 'DO_LOOUT';
export const DO_SIGNUP = 'DO_SIGNUP'; export const DO_SIGNUP = 'DO_SIGNUP';
@@ -40,6 +46,7 @@ export const LINK_GOOGLE_FAILURE = 'LINK_GOOGLE_FAILURE';
export const LINK_GOOGLE_SUCCESS = 'LINK_GOOGLE_SUCCESS'; export const LINK_GOOGLE_SUCCESS = 'LINK_GOOGLE_SUCCESS';
export const SET_PROFILE = 'SET_PROFILE'; export const SET_PROFILE = 'SET_PROFILE';
export const UNSET_PROFILE = 'UNSET_PROFILE';
export const UPDATE_PROFILE = 'UPDATE_PROFILE'; export const UPDATE_PROFILE = 'UPDATE_PROFILE';
export const SET_NOM_DE_BID = 'SET_NOM_DE_BID'; export const SET_NOM_DE_BID = 'SET_NOM_DE_BID';

View File

@@ -13,7 +13,7 @@ export default class Post extends Record({
Post.fromJS = (data = {}) => { Post.fromJS = (data = {}) => {
return new TicketClass({ return new Post({
id: data._id, id: data._id,
...data, ...data,
}); });

View File

@@ -1,7 +1,7 @@
import { SET_ITEM_FILTER } from '../constants/actionTypes.js'; import { SET_AUCTION_FILTER } from '../constants/actionTypes.js';
import { ITEM_FILTERS } from '../constants/constants.js'; import { ITEM_FILTERS } from '../constants/constants.js';
export const itemFilter = (state = ITEM_FILTERS.ALL, action) => { export const auctionFilter = (state = ITEM_FILTERS.ALL, action) => {
switch (action.type) { switch (action.type) {
case SET_AUCTION_FILTER: case SET_AUCTION_FILTER:
return action.payload; return action.payload;

14
app/reducers/auth.js Normal file
View File

@@ -0,0 +1,14 @@
import { LOGIN_SUCCESS, SET_AUTH, UNSET_AUTH } from '../constants/actionTypes.js';
export const auth = (state = null, action) => {
switch (action.type) {
case LOGIN_SUCCESS:
return action.payload.token;
case SET_AUTH:
return action.payload;
case UNSET_AUTH:
return null;
default:
return state;
}
};

View File

@@ -5,12 +5,11 @@ import { EVENTS_LOADED, GET_EVENTS } from '../constants/actionTypes.js';
export const events = (state = new Map(), action) => { export const events = (state = new Map(), action) => {
switch (action.type) { switch (action.type) {
case EVENTS_LOADED: case EVENTS_LOADED:
return state.merge( const mapped = action.payload.toMap().mapEntries((entry) => {
action.payload.toMap().mapEntries((entry) => {
const [, event] = entry; const [, event] = entry;
return [`${event.id}`, event]; return [`${event.id}`, event];
}), });
); return state.merge(mapped);
case GET_EVENTS: case GET_EVENTS:
default: default:
return state; return state;

View File

@@ -5,9 +5,11 @@ import { activeItem } from './activeItem.js';
import { auctionFilter } from './auctionFilter.js'; import { auctionFilter } from './auctionFilter.js';
import { auctions } from './auctions.js'; import { auctions } from './auctions.js';
import { auctionView } from './auctionView.js'; import { auctionView } from './auctionView.js';
import { auth } from './auth.js';
import { blockUI } from './blockUI.js'; import { blockUI } from './blockUI.js';
import { events } from './events.js'; import { events } from './events.js';
import { items } from './items.js'; import { items } from './items.js';
import { profile } from './profile.js';
const rootReducer = combineReducers({ const rootReducer = combineReducers({
activeEvent, activeEvent,
@@ -15,9 +17,11 @@ const rootReducer = combineReducers({
auctionFilter, auctionFilter,
auctions, auctions,
auctionView, auctionView,
auth,
blockUI, blockUI,
events, events,
items, items,
profile,
}); });
export default rootReducer; export default rootReducer;

View File

@@ -1,14 +1,20 @@
import { Map } from 'immutable'; import { Map } from 'immutable';
import { import {
LOGIN_SUCCESS,
SET_PROFILE, SET_PROFILE,
UNSET_PROFILE,
UPDATE_PROFILE, UPDATE_PROFILE,
} from '../constants/actionTypes.js'; } from '../constants/actionTypes.js';
export const profile = (state = new Map(), action) => { export const profile = (state = new Map(), action) => {
switch (action.type) { switch (action.type) {
case LOGIN_SUCCESS:
return action.payload.user;
case SET_PROFILE: case SET_PROFILE:
return action.payload; return action.payload;
case UNSET_PROFILE:
return new Map();
case UPDATE_PROFILE: case UPDATE_PROFILE:
return action.payload; return action.payload;
default: default:

View File

@@ -3,9 +3,9 @@ import { Dimensions, Platform } from 'react-native';
import { createBottomTabNavigator, createStackNavigator } from 'react-navigation'; import { createBottomTabNavigator, createStackNavigator } from 'react-navigation';
import { Icon } from 'react-native-elements'; import { Icon } from 'react-native-elements';
import AppHeader from './Components/AppHeader/AppHeader.js'; import AppHeader from './components/AppHeader/AppHeader.js';
import Auction from './screen/Auction.container.js'; import Auction from './screens/Auction.container.js';
import Checkout from './screens/Checkout.js'; import Checkout from './screens/Checkout.js';
import Event from './screens/Event.container.js'; import Event from './screens/Event.container.js';
import Events from './screens/Events.container.js'; import Events from './screens/Events.container.js';
@@ -50,43 +50,43 @@ export const Tabs = createBottomTabNavigator({
export const SignInOrRegisterStack = createStackNavigator({ export const SignInOrRegisterStack = createStackNavigator({
SignInOrRegister: { SignInOrRegister: {
screen: SignInOrRegister, screen: SignInOrRegister,
navigationOptions: ({ navigation }) => { navigationOptions: ({ navigation }) => ({
header: null, header: null,
tabBarVisible: false, tabBarVisible: false,
gesturesEnabled: false gesturesEnabled: false,
}, }),
}, },
Register: { Register: {
screen: Register, screen: Register,
navigationOptions: ({ navigation }) => { navigationOptions: ({ navigation }) => ({
header: null, header: null,
tabBarVisible: false, tabBarVisible: false,
gesturesEnabled: false gesturesEnabled: false,
}, }),
}, },
}); });
export const AuctionStack = createStackNavigator({ export const AuctionStack = createStackNavigator({
Auction: { Auction: {
screen: Auction, screen: Auction,
navigationOptions: ({navigation}) => ({ navigationOptions: ({ navigation }) => ({
header: <AppHeader navigation={navigation} />, header: <AppHeader navigation={navigation} />,
}), }),
}, },
Item: { Item: {
screen: Item, screen: Item,
navigationOptions: ({navigation}) => ({ navigationOptions: ({ navigation }) => ({
header: null, header: null,
tabBarVisible: false, tabBarVisible: false,
gesturesEnabled: false gesturesEnabled: false,
}), }),
}, },
ImageDetail: { ImageDetail: {
screen: ImageDetail, screen: ImageDetail,
navigationOptions: ({navigation}) => ({ navigationOptions: ({ navigation }) => ({
header: null, header: null,
tabBarVisible: false, tabBarVisible: false,
gesturesEnabled: false gesturesEnabled: false,
}), }),
}, },
}); });
@@ -94,32 +94,32 @@ export const AuctionStack = createStackNavigator({
export const BazaarStack = createStackNavigator({ export const BazaarStack = createStackNavigator({
Bazaar: { Bazaar: {
screen: Marketplace, screen: Marketplace,
navigationOptions: ({navigation}) => ({ navigationOptions: ({ navigation }) => ({
header: <AppHeader navigation={navigation} />, header: <AppHeader navigation={navigation} />,
}), }),
}, },
Item: { Item: {
screen: Item, screen: Item,
navigationOptions: ({navigation}) => ({ navigationOptions: ({ navigation }) => ({
header: null, header: null,
tabBarVisible: false, tabBarVisible: false,
gesturesEnabled: false gesturesEnabled: false,
}), }),
}, },
ImageDetail: { ImageDetail: {
screen: ImageDetail, screen: ImageDetail,
navigationOptions: ({navigation}) => ({ navigationOptions: ({ navigation }) => ({
header: null, header: null,
tabBarVisible: false, tabBarVisible: false,
gesturesEnabled: false gesturesEnabled: false,
}), }),
}, },
Checkout: { Checkout: {
screen: Checkout, screen: Checkout,
navigationOptions: ({navigation}) => ({ navigationOptions: ({ navigation }) => ({
header: null, header: null,
tabBarVisible: false, tabBarVisible: false,
gesturesEnabled: false gesturesEnabled: false,
}), }),
}, },
}); });
@@ -130,7 +130,7 @@ export const EventsStack = createStackNavigator({
navigationOptions: ({ navigation }) => ({ navigationOptions: ({ navigation }) => ({
header: <AppHeader navigation={navigation} />, header: <AppHeader navigation={navigation} />,
tabBarVisible: false, tabBarVisible: false,
gesturesEnabled: false gesturesEnabled: false,
}), }),
} }
}); });
@@ -141,30 +141,30 @@ export const createRootNavigator = () => {
AuctionStack: { AuctionStack: {
screen: AuctionStack, screen: AuctionStack,
navigationOptions: { navigationOptions: {
gesturesEnabled: false gesturesEnabled: false,
} }
}, },
BazaarStack: { BazaarStack: {
screen: BazaarStack, screen: BazaarStack,
navigationOptions: { navigationOptions: {
gesturesEnabled: false gesturesEnabled: false,
} }
}, },
EventsStack: { EventsStack: {
screen: EventsStack, screen: EventsStack,
navigationOptions: { navigationOptions: {
gesturesEnabled: false gesturesEnabled: false,
} }
}, },
Tabs: { Tabs: {
screen: Tabs, screen: Tabs,
navigationOptions: { navigationOptions: {
gesturesEnabled: false gesturesEnabled: false,
} }
} }
}, },
{ {
mode: "modal" mode: "modal",
} }
); );
}; };

View File

@@ -1,34 +1,34 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { fetchEvent } from '../actions/events.js'; import { fetchEvents } from '../actions/events.js';
import { getEventById } from '../selectors/events.js'; import EventRecord from '../domain/Event.js';
import { getActiveEvent, getDefaultEvent } from '../selectors/events.js';
import Event from './Event.js'; import Event from './Event.js';
const matchStateToProps = (state) => { const matchStateToProps = (state) => {
const eventId = state.get('activeEvent'); const event = getActiveEvent(state) || getDefaultEvent(state) || new EventRecord();
const event = getEventById(state, eventId);
return { return {
description: event.get('description'), description: event.get('description'),
endTime: event.get('endTime'), endTime: event.get('endTime'),
id: event.get('id'), id: event.get('id'),
images: event.get('images'), images: event.get('images').toArray(),
isTicketed: event.get('isTicketed'), isTicketed: event.get('isTicketed'),
posts: event.get('posts'), posts: event.get('posts').toArray(),
requireLoginToSeeAuction: event.get('requireLoginToSeeAuction'), requireLoginToSeeAuction: event.get('requireLoginToSeeAuction'),
showFrom: event.get('showFrom'), showFrom: event.get('showFrom'),
showUntil: event.get('showUntil'), showUntil: event.get('showUntil'),
startTime: event.get('startTime'), startTime: event.get('startTime'),
tagline: event.get('tagline'), tagline: event.get('tagline'),
ticketClasses: event.get('ticketClasses'), ticketClasses: event.get('ticketClasses').toArray(),
title: event.get('title'), title: event.get('title'),
url: event.get('url'), url: event.get('url'),
}; };
}; };
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
fetchEvent: () => dispatch(fetchEvent(dispatch)), fetchEvents: () => dispatch(fetchEvents()),
}); });
export default connect(matchStateToProps, mapDispatchToProps)(Event); export default connect(matchStateToProps, mapDispatchToProps)(Event);

View File

@@ -1,4 +1,5 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Text, View } from 'react-native'; import { Text, View } from 'react-native';
import styles from './Event.styles.js'; import styles from './Event.styles.js';
@@ -6,10 +7,10 @@ import styles from './Event.styles.js';
export default class Event extends Component { export default class Event extends Component {
static get propTypes() { static get propTypes() {
return { return {
description: PropTypes.string.isRequired, description: PropTypes.string,
endTime: PropTypes.string.isRequired, endTime: PropTypes.string,
fetchEvent: PropTypes.func.isRequired, fetchEvents: PropTypes.func,
id: PropTypes.string.isRequired, id: PropTypes.string,
images: PropTypes.arrayOf( images: PropTypes.arrayOf(
PropTypes.shape({ PropTypes.shape({
url: PropTypes.string, url: PropTypes.string,
@@ -18,27 +19,27 @@ export default class Event extends Component {
isTicketed: PropTypes.bool, isTicketed: PropTypes.bool,
posts: PropTypes.arrayOf( posts: PropTypes.arrayOf(
PropTypes.shape({ PropTypes.shape({
author: PropTypes.string.isRequired, author: PropTypes.string,
content: PropTypes.string.isRequired, content: PropTypes.string,
id: PropTypes.string.isRequired, id: PropTypes.string,
isPublic: PropTypes.bool, isPublic: PropTypes.bool,
scheduledPost: PropTypes.bool, scheduledPost: PropTypes.bool,
sendNotification: PropTypes.bool, sendNotification: PropTypes.bool,
timestamp: PropTypes.string.isRequired, timestamp: PropTypes.string,
title: PropTypes.string.isRequired, title: PropTypes.string,
}), }),
), ),
requireLoginToSeeAuction: PropTypes.bool.isRequired, requireLoginToSeeAuction: PropTypes.bool,
showFrom: PropTypes.string.isRequired, showFrom: PropTypes.string,
showUntil: PropTypes.string.isRequired, showUntil: PropTypes.string,
startTime: PropTypes.string.isRequired, startTime: PropTypes.string,
tagline: PropTypes.string, tagline: PropTypes.string,
ticketClasses: PropTypes.arrayOf( ticketClasses: PropTypes.arrayOf(
PropTypes.shape({ PropTypes.shape({
}), }),
) ),
title: PropTypes.string.isRequired, title: PropTypes.string,
url: PropTypes.string, url: PropTypes.string,
}; };
} }
@@ -59,6 +60,10 @@ export default class Event extends Component {
super(props); super(props);
} }
componentDidMount() {
this.props.fetchEvents();
}
render() { render() {
const { const {
description, description,

View File

@@ -1,6 +1,6 @@
import { StyleSheet } from 'react-native'; import { StyleSheet } from 'react-native';
export default const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
justifyContent: 'center', justifyContent: 'center',
@@ -13,3 +13,5 @@ export default const styles = StyleSheet.create({
margin: 10, margin: 10,
} }
}); });
export default styles;

View File

@@ -1,30 +1,38 @@
import { List } from 'immutable';
import React, { Component } from 'react'; import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { import {
FlatList, FlatList,
StyleSheet,
Text, Text,
View, View,
} from 'react-native'; } from 'react-native';
import { SORT_MODES, VIEW_MODES } from '../constants/constants.js'; import { SORT_MODES, AUCTION_VIEW_MODES } from '../constants/constants.js';
import GridItem from '../components/Item/Grid.js'; import FilterBar from '../components/Auction/FilterBar.js';
import ListItem from '../components/Item/List.js'; import AuctionListItem from '../containers/Auction/AuctionListItem.js';
//import styles from './Marketplace.styles.js';
export default class Marketplace extends Component { export default class Marketplace extends Component {
static get propTypes() { static get propTypes() {
return { return {
changeFilter: PropTypes.func, changeFilter: PropTypes.func,
items: PropTypes.array.isRequired, changeViewMode: PropTypes.func.isRequired,
fetchItems: PropTypes.func.isRequired,
fetchStatus: PropTypes.func.isRequired,
items: PropTypes.oneOfType([
PropTypes.array,
PropTypes.instanceOf(List),
]),
}; };
} }
static get defaultProps() { static get defaultProps() {
return { return {
changeFilter: () => { console.log('Change Filter Default Prop', arguments); }, changeFilter: () => { console.log('Change Filter Default Prop', arguments); },
header: null, items: [],
}; };
} }
@@ -36,57 +44,42 @@ export default class Marketplace extends Component {
this.state = { this.state = {
sort: SORT_MODES.TITLE_ASC, sort: SORT_MODES.TITLE_ASC,
view: VIEW_MODES.LIST, view: AUCTION_VIEW_MODES.ALL,
}; };
} }
componentDidMount() {
this.props.fetchStatus();
this.props.fetchItems();
}
changeFilter(filter) { changeFilter(filter) {
this.props.changeFilter('market', filter); this.props.changeFilter('auction', filter);
} }
changeViewMode(mode) { _keyExtractor = (item, index) => `${item._id}_${index}`;
this.setState({ view: mode });
}
_keyExtractor = (item, index) => item.id; _renderAuctionListItem = ({ item }) => <AuctionListItem item={item} />;
_renderItem = (view) => ({ item }) => {
if (view === VIEW_MODES.GRID) {
return <GridItem {...item} />;
}
return <ListItem {...item} />;
}
render() { render() {
const { items, view } = this.state; const { items } = this.props;
const { sort, view } = this.state;
return ( return (
<View style={styles.container}> <View style={styles.container}>
<FilterBar <FilterBar
changeFilterer={this.changeFilter} changeFilterer={this.changeFilter}
changeViewMode={this.changeViewMode}
/>
<FlatList
data={items}
keyExtractor={this._keyExtractor}
renderItem={this.renderItem(view)}
/> />
{items.size > 0 && (
<FlatList
data={items}
keyExtractor={this._keyExtractor}
renderItem={this._renderAuctionListItem}
contentContainerStyle={styles.itemListContentContainer}
style={styles.itemList}
/>
)}
</View> </View>
); );
} }
} }
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
title: {
fontSize: 20,
textAlign: 'center',
margin: 10,
}
});

View File

@@ -0,0 +1,16 @@
import { StyleSheet } from 'react-native';
export const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
},
title: {
},
localLogin: {
},
services: {
},
register: {
},
});

View File

@@ -1,7 +1,9 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Text, View } from 'react-native'; import { Text, View } from 'react-native';
import { Button } from 'react-native-elements';
import FacebookLogin from '../components/FacebookLogin/FacebookLogin.container.js'; import FacebookLogin from '../components/Login/FacebookLogin.container.js';
import LocalLogin from '../components/Login/LocalLogin.container.js';
import styles from './SignInOrRegister.styles.js'; import styles from './SignInOrRegister.styles.js';
@@ -28,7 +30,7 @@ export default class SignInOrRegister extends Component {
<FacebookLogin /> <FacebookLogin />
</View> </View>
<View style={styles.register}> <View style={styles.register}>
<Button onPress={this._doRegistration} /> <Button title="Signup with Email" onPress={this._doRegistration} />
</View> </View>
</View> </View>
); );

View File

@@ -1,6 +1,6 @@
import { StyleSheet } from 'react-native'; import { StyleSheet } from 'react-native';
export default const styles = StyleSheet.create({ export const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
justifyContent: 'center', justifyContent: 'center',

View File

@@ -8,6 +8,6 @@ export const getActiveEventId = createSelector(
); );
export const hasActiveEvent = createSelector( export const hasActiveEvent = createSelector(
[getState], [getActiveEventId],
(state) => !!state.get('activeEvent'), (eventId) => !!eventId,
); );

View File

@@ -19,15 +19,15 @@ export const getAuctionStatuses = createSelector(
export const getItemsIdsWithNoBids = createSelector( export const getItemsIdsWithNoBids = createSelector(
[getAuctionStatuses], [getAuctionStatuses],
(auctions) => auctions.filter(auction => auction.bidCount === 0); (auctions) => auctions.filter(auction => auction.bidCount === 0),
); );
export const getMyBidItemIds = createSelector( export const getMyBidItemIds = createSelector(
[getAuctionStatuses], [getAuctionStatuses],
(auctions) => auctions.filter(auction => auction.isBidding); (auctions) => auctions.filter(auction => auction.isBidding),
); );
export const getMyWinningItemIds = createSelector( export const getMyWinningItemIds = createSelector(
[getAuctionStatuses], [getAuctionStatuses],
(auctions) => auctions.filter(auction => auction.isWinning); (auctions) => auctions.filter(auction => auction.isWinning),
); );

8
app/selectors/auth.js Normal file
View File

@@ -0,0 +1,8 @@
import { createSelector } from 'reselect';
const getState = (state) => state;
export const getAuthToken = createSelector(
[getState],
(state) => state.get('auth'),
);

View File

@@ -14,7 +14,7 @@ export const getEvents = createSelector(
export const getActiveEvent = createSelector( export const getActiveEvent = createSelector(
[getActiveEventId, getEvents], [getActiveEventId, getEvents],
(eventId, eventsAsMap) => eventId ? eventsAsMap.get(eventId) : false, (eventId, eventsAsMap) => eventId ? eventsAsMap.get(eventId) : null,
); );
export const getDefaultEvent = createSelector( export const getDefaultEvent = createSelector(