14 Commits

Author SHA1 Message Date
e0dd85153c - modal component 2020-05-19 00:48:11 -04:00
Mike Fitzpatrick
ee051f604e - Login handling 2019-08-20 15:04:06 -04:00
Mike Fitzpatrick
cc8442b0b2 - Breaking up the profile 2019-08-17 02:45:57 -04:00
Mike Fitzpatrick
c146884636 - Wiring up nom and phone registration bits
- Added guided registration
2019-08-14 11:18:21 -04:00
Mike Fitzpatrick
cf07ab1c2e Merge branch 'feature/PROFILE_-_Registration' of honey.fitz.guru:eventment-app into feature/PROFILE_-_Registration 2019-08-12 17:44:42 -04:00
Mike Fitzpatrick
f0460a1b76 - more 2019-08-12 17:44:01 -04:00
769fc0c2b5 - getDefaultEvent now returns active event or first event 2019-08-10 15:28:12 -04:00
0f618fdd78 - Reorg andf cleanup. Maintainability. 2019-08-09 02:52:31 -04:00
e6a4eeaeb1 - more tweaks and fixes.... still not running 2019-08-09 02:01:39 -04:00
e07a4cb2b9 - ignore lock files 2019-08-08 20:59:40 -04:00
70a8246a05 Merge branch 'feature/PROFILE_-_Registration' of honey.fitz.guru:eventment-app into feature/PROFILE_-_Registration
# Conflicts:
#	app/components/AppHeader/HeaderContentLeft.js
#	app/components/AppHeader/HeaderTitle/EventTitle/EventTitle.js
2019-08-08 20:58:01 -04:00
2643fe5d4a - conlicts inboud... 2019-08-08 20:55:19 -04:00
Mike Fitzpatrick
ebb6f47455 - The fix is in! Linty fresh and pretty... 2019-08-08 16:18:08 -04:00
Mike Fitzpatrick
dfc4daf696 - Stuff 2019-08-07 17:49:34 -04:00
78 changed files with 1787 additions and 7483 deletions

1
.gitignore vendored
View File

@@ -57,3 +57,4 @@ buck-out/
# CocoaPods # CocoaPods
/ios/Pods/ /ios/Pods/
*.lock

View File

@@ -1,6 +1,6 @@
import { List } from 'immutable'; import { List } from 'immutable';
import { fetchAuctionStatus as fetchActionStatusApi } from '../api/actionStatus.js'; import { fetchAuctionStatus as fetchActionStatusApi } from '../api/auctionStatus.js';
import { AUCTIONS_UPDATED } from '../constants/actionTypes.js'; import { AUCTIONS_UPDATED } from '../constants/actionTypes.js';
import { getActiveEventId } from '../selectors/activeEvent.js'; import { getActiveEventId } from '../selectors/activeEvent.js';
import { getAuthToken } from '../selectors/auth.js'; import { getAuthToken } from '../selectors/auth.js';

View File

@@ -5,6 +5,7 @@ import {
getNomAvailaibility, getNomAvailaibility,
loginUser, loginUser,
registerNewUser, registerNewUser,
setNomDeBid as setNomDeBidApi,
} from '../api/profile.js'; } from '../api/profile.js';
import { import {
@@ -15,11 +16,22 @@ import {
PROFILE_NOM_AVAILABLE, PROFILE_NOM_AVAILABLE,
REGISTRATION_FAILURE, REGISTRATION_FAILURE,
REGISTRATION_SUCCESS, REGISTRATION_SUCCESS,
SET_NOM_FAILURE,
SET_NOM_SUCCESS,
UNSET_AUTH, UNSET_AUTH,
UNSET_PROFILE, UNSET_PROFILE,
UPDATE_PROFILE, UPDATE_PROFILE,
} from '../constants/actionTypes.js'; } from '../constants/actionTypes.js';
import { getAuthToken } from '../selectors/auth.js';
import { getUserId } from '../selectors/profile.js';
const handleLoginFailure = (errors) => (dispatch) => {
dispatch(loginFailure(errors));
dispatch(unblockUI());
};
const isValidEmail = (payload) => ({ const isValidEmail = (payload) => ({
type: PROFILE_EMAIL_AVAILABLE, type: PROFILE_EMAIL_AVAILABLE,
payload, payload,
@@ -44,6 +56,16 @@ const logoutUser = () => ({
type: DO_LOGOUT, type: DO_LOGOUT,
}); });
export const setNomFailure = ({ info }) => ({
type: SET_NOM_FAILURE,
payload: info,
});
export const setNomSuccess = ({ nomDeBid }) => ({
type: SET_NOM_SUCCESS,
payload: nomDeBid,
});
const registrationFailure = (payload) => ({ const registrationFailure = (payload) => ({
type: REGISTRATION_FAILURE, type: REGISTRATION_FAILURE,
payload, payload,
@@ -71,13 +93,28 @@ export const checkEmailAvailability = (email) => (dispatch) => {};
export const checkNomAvailability = (nomDeBid) => (dispatch) => {}; export const checkNomAvailability = (nomDeBid) => (dispatch) => {};
export const setNomDeBid = ({ nomDeBid }) => (dispatch, getState) => {
const id = getUserId(getState());
const auth = getAuthToken(getState());
setNomDeBidApi({ id, nomDeBid }, auth)
.then((result) => dispatch(setNomSuccess(result)))
.catch((err) => dispatch(setNomFailure(err)));
};
export const login = (username, password) => (dispatch) => { export const login = (username, password) => (dispatch) => {
dispatch(blockUI()); dispatch(blockUI());
loginUser(username, password) loginUser(username, password)
.then((result) => { .then((result) => {
if (result.errors) {
dispatch(handleLoginFailure(result.errors));
return;
}
dispatch(loginSuccess(result)); dispatch(loginSuccess(result));
dispatch(unblockUI());
}) })
.catch((err) => dispatch(loginFailure(err))); .catch((err) => dispatch(handleLoginFailure(err)));
}; };
export const logout = () => (dispatch) => { export const logout = () => (dispatch) => {

View File

@@ -33,6 +33,10 @@ export const formatPostData = (body) => {
return postData; return postData;
}; };
export const formatJsonData = (body) => {
return JSON.stringify(body);
};
const parseQueryParamsString = (queryParams) => { const parseQueryParamsString = (queryParams) => {
if (typeof queryParams !== 'string') { if (typeof queryParams !== 'string') {
return null; return null;

View File

@@ -1,6 +1,7 @@
import { import {
constructUrl, constructUrl,
formatPostData, formatPostData,
formatJsonData,
parseQueryParams, parseQueryParams,
request, request,
unwrapJson, unwrapJson,
@@ -24,6 +25,7 @@ export const API_ENDPOINTS = {
PURCHASE_ITEM: '/sales', PURCHASE_ITEM: '/sales',
// User/Profile // User/Profile
LOGIN: '/auth',
USER_SIGNUP: '/signup', USER_SIGNUP: '/signup',
USER_PROFILE: '/users/:user_id', USER_PROFILE: '/users/:user_id',
@@ -45,11 +47,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 = {}) => {
@@ -74,9 +76,15 @@ export const requestPost = (options) => {
queryParams = [], queryParams = [],
requestOptions = {}, requestOptions = {},
isFormattedPostData = false, isFormattedPostData = false,
shouldUseFormData = false,
} = options; } = options;
const params = parseQueryParams(queryParams || []); const params = parseQueryParams(queryParams || []);
let postData = isFormattedPostData ? body : null;
if (!postData) {
postData = shouldUseFormData ? formatPostData(body) : formatJsonData(body);
}
if (params === null) { if (params === null) {
throw new Error('invalid queryParams'); throw new Error('invalid queryParams');
@@ -88,7 +96,7 @@ export const requestPost = (options) => {
...DefaultRequestOptions, ...DefaultRequestOptions,
...requestOptions, ...requestOptions,
method: 'POST', method: 'POST',
body: isFormattedPostData ? body : formatPostData(body), body: postData,
}) })
.then(validateResponse) .then(validateResponse)
.then(unwrapJson); .then(unwrapJson);

View File

@@ -1,7 +1,7 @@
import { API_ENDPOINTS, requestGet } from './index.js'; import { API_ENDPOINTS, requestGet, requestPost } from './index.js';
export const getEmailAvailability = (email) => export const getEmailAvailability = (email) =>
requestGet(`${API_ENDPOINTS.VALIDATE_SIGNUP_EMAIL}/&{encodeURI(email)}`); requestGet(`${API_ENDPOINTS.VALIDATE_SIGNUP_EMAIL}/${encodeURI(email)}`);
export const getNomAvailaibility = (nomDeBid) => export const getNomAvailaibility = (nomDeBid) =>
requestGet(`${API_ENDPOINTS.VALIDATE_SIGNUP_NOM}/${encodeURI(nomDeBid)}`); requestGet(`${API_ENDPOINTS.VALIDATE_SIGNUP_NOM}/${encodeURI(nomDeBid)}`);
@@ -17,3 +17,10 @@ export const registerNewUser = (user) =>
path: API_ENDPOINTS.USER_SIGNUP, path: API_ENDPOINTS.USER_SIGNUP,
body: { user }, body: { user },
}); });
export const setNomDeBid = (id, auth) => (nomDeBid) =>
requestPost({
path: `${API_ENDPOINTS.SET_NOM}/${id}`,
body: { nomDeBid },
requestOptions: { Authorization: auth ? `Bearer ${auth}` : null },
});

View File

@@ -7,8 +7,6 @@ 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';
export default class AppHeader extends Component { export default class AppHeader extends Component {
static get propTypes() { static get propTypes() {
return { return {
@@ -26,9 +24,9 @@ export default class AppHeader extends Component {
return ( return (
<Header <Header
placement="left" placement="left"
leftComponent={<HeaderContentRight navigation={navigation} />}
centerComponent={<HeaderTitle navigation={navigation} />} centerComponent={<HeaderTitle navigation={navigation} />}
rightComponent={<HeaderContentLeft navigation={navigation} />} leftComponent={<HeaderContentLeft navigation={navigation} />}
rightComponent={<HeaderContentRight navigation={navigation} />}
/> />
); );
} }

View File

@@ -1,12 +1,15 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Text, TouchableOpacity, View } from 'react-native';
import BackIcon from './IconButtons/BackIcon.js'; import BackIcon from './IconButtons/BackIcon.js';
import EventsIcon from './IconButtons/EventsIcon.js'; import EventsIcon from './IconButtons/EventsIcon.js';
export default function HeaderContentLeft({ activeRoute, hasMultipleEvents, navigation }) { export default function HeaderContentLeft({
activeRoute,
hasActiveEvent,
hasMultipleEvents,
navigation,
}) {
const _goBack = () => { const _goBack = () => {
if (hasActiveEvent) { if (hasActiveEvent) {
navigation.goBack(); navigation.goBack();
@@ -29,15 +32,20 @@ export default function HeaderContentLeft({ activeRoute, hasMultipleEvents, navi
return <BackIcon action={_goBack} />; return <BackIcon action={_goBack} />;
} }
return <EventsIcon action={hasMultipleEvents ? _showEvents : null} />; if (hasMultipleEvents) {
return <EventsIcon action={_showEvents} />;
}
return null;
} }
HeaderContentLeft.propTypes = { HeaderContentLeft.propTypes = {
activeRoute: PropTypes.string.isRequired, activeRoute: PropTypes.string.isRequired,
hasActiveEvent: PropTypes.bool, hasActiveEvent: PropTypes.bool,
navigation: PropTypes.func.isRequired, hasMultipleEvents: PropTypes.bool,
}; };
HeaderContentLeft.defaultProps = { HeaderContentLeft.defaultProps = {
hasActiveEvent: false, hasActiveEvent: false,
hasMultipleEvents: false,
}; };

View File

@@ -6,7 +6,9 @@ import HeaderContentRight from './HeaderContentRight.js';
const matchStateToProps = (state, ownProps) => ({ const matchStateToProps = (state, ownProps) => ({
avatarUrl: getProfileAvatarUrl(state), avatarUrl: getProfileAvatarUrl(state),
hideUserProfileButton: ownProps.navigation.state.routeName === 'Profile', hideUserProfileButton:
['Profile', 'Register', 'SignInOrRegister'].indexOf(ownProps.navigation.state.routeName) >
-1,
}); });
export default connect( export default connect(

View File

@@ -8,7 +8,7 @@ export default function HeaderContentRight({ hideUserProfileButton, navigation }
return null; return null;
} }
return <UserProfileButton />; return <UserProfileButton navigation={navigation} />;
} }
HeaderContentRight.propTypes = { HeaderContentRight.propTypes = {

View File

@@ -1,5 +1,6 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { hasActiveEvent } from '../../../../selectors/activeEvent.js';
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

@@ -6,12 +6,16 @@ import { Text, TouchableOpacity, View } from 'react-native';
import styles from './EventTitle.styles.js'; import styles from './EventTitle.styles.js';
export default function EventTitle({ action, date, end, name, start }) { export default function EventTitle({ action, date, end, name, start }) {
const _generateEventTitle = () => ( const _generateEventTitle = () => {
<View style={styles.eventInfo}> const whenString = `${date} | ${start} - ${end}`;
<Text style={styles.eventName}>{name}</Text>
<Text style={styles.eventDate}>{`${date} | ${start} - ${end}`}</Text> return (
</View> <View style={styles.eventInfo}>
); <Text style={styles.eventName}>{name}</Text>
<Text style={styles.eventDate}>{whenString}</Text>
</View>
);
};
if (action) { if (action) {
return <TouchableOpacity onPress={action}>{_generateEventTitle()}</TouchableOpacity>; return <TouchableOpacity onPress={action}>{_generateEventTitle()}</TouchableOpacity>;

View File

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

View File

@@ -1,12 +1,17 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Text, TouchableOpacity, View } from 'react-native'; import { Text, TouchableOpacity } from 'react-native';
import EventTitle from './EventTitle/EventTitle.container.js'; import EventTitle from './EventTitle/EventTitle.container.js';
import styles from './HeaderTitle.styles.js'; import styles from './HeaderTitle.styles.js';
const STRINGS = {
EVENTS: 'Events',
PROFILE: 'Profile',
};
export default function HeaderTitle({ export default function HeaderTitle({
activeRoute, activeRoute,
hasActiveEvent, hasActiveEvent,
@@ -30,13 +35,13 @@ export default function HeaderTitle({
if (activeRoute === 'Events') { if (activeRoute === 'Events') {
return ( return (
<TouchableOpacity onPress={_goBack}> <TouchableOpacity onPress={_goBack}>
<Text style={styles.screenHeader}>Profile</Text> <Text style={styles.screenHeader}>{STRINGS.EVENTS}</Text>
</TouchableOpacity> </TouchableOpacity>
); );
} }
if (activeRoute === 'Profile') { if (activeRoute === 'Profile') {
return <Text style={styles.screenHeader}>Profile</Text>; return <Text style={styles.screenHeader}>{STRINGS.PROFILE}</Text>;
} }
return <EventTitle action={hasMultipleEvents ? _showEvents : null} />; return <EventTitle action={hasMultipleEvents ? _showEvents : null} />;
@@ -46,7 +51,6 @@ HeaderTitle.propTypes = {
activeRoute: PropTypes.string.isRequired, activeRoute: PropTypes.string.isRequired,
hasActiveEvent: PropTypes.bool, hasActiveEvent: PropTypes.bool,
hasMultipleEvents: PropTypes.bool.isRequired, hasMultipleEvents: PropTypes.bool.isRequired,
navigation: PropTypes.func.isRequired,
}; };
HeaderTitle.defaultProps = { HeaderTitle.defaultProps = {

View File

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

View File

@@ -2,12 +2,12 @@ import React from 'react';
import PropTypes from 'prop-types'; 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-vector-icons';
export default function BackIcon({ action }) { export default function BackIcon({ action }) {
return ( return (
<TouchableOpacity onPress={action}> <TouchableOpacity onPress={action}>
<Icon name="ei-chevron-left" type="evilicons" size={28} />; <Icon name="ei-chevron-left" type="evilicons" size={28} />
</TouchableOpacity> </TouchableOpacity>
); );
} }

View File

@@ -5,7 +5,7 @@ import { TouchableOpacity } from 'react-native';
import { Icon } from 'react-native-elements'; import { Icon } from 'react-native-elements';
export default function EventsIcon({ action }) { export default function EventsIcon({ action }) {
const renderEventsIcon = () => <Icon name="ei-calendar" type="evilicons" size={28} />; const renderEventsIcon = () => <Icon name="calendar" type="evilicon" size={28} />;
if (action) { if (action) {
return <TouchableOpacity onPress={action}>{renderEventsIcon()}</TouchableOpacity>; return <TouchableOpacity onPress={action}>{renderEventsIcon()}</TouchableOpacity>;

View File

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

View File

@@ -2,22 +2,33 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Image, TouchableOpacity, View } from 'react-native'; import { Image, TouchableOpacity, View } from 'react-native';
import { Icon } from 'react-native-elements'; import { Avatar, Icon } from 'react-native-elements';
import styles from './UserProfileButton.styles.js'; import styles from './UserProfileButton.styles.js';
export default function UserProfileButton({ avatarUrl, navigation }) { export default function UserProfileButton({
avatarUrl,
initials,
isRegisteredAccount,
navigation,
}) {
const _goToProfile = () => { const _goToProfile = () => {
navigation.navigate('Profile'); if (isRegisteredAccount) {
return false; navigation.navigate('Profile');
return false;
}
navigation.navigate('SignInOrRegister');
}; };
return ( return (
<TouchableOpacity onPress={_goToProfile}> <TouchableOpacity onPress={_goToProfile}>
{avatarUrl !== null ? ( {isRegisteredAccount !== null ? (
<View style={styles.avatarWrap}> avatarUrl !== null ? (
<Image source={{ uri: avatarUrl }} /> <Avatar source={{ uri: avatarUrl }} />
</View> ) : (
<Avatar title={initials} />
)
) : ( ) : (
<Icon name="ei-user" type="evilicons" size={28} /> <Icon name="ei-user" type="evilicons" size={28} />
)} )}
@@ -27,8 +38,12 @@ export default function UserProfileButton({ avatarUrl, navigation }) {
UserProfileButton.propTypes = { UserProfileButton.propTypes = {
avatarUrl: PropTypes.string, avatarUrl: PropTypes.string,
initials: PropTypes.string,
isRegisteredAccount: PropTypes.bool,
}; };
UserProfileButton.propTypes = { UserProfileButton.defaultProps = {
avatarUrl: null, avatarUrl: null,
initials: null,
isRegisteredAccount: false,
}; };

View File

@@ -2,7 +2,7 @@ import { connect } from 'react-redux';
import { placeBid } from '../../actions/auction.js'; import { placeBid } from '../../actions/auction.js';
import AuctionListItem from '../../components/Auction/AuctionListItem.js'; import AuctionListItem from './AuctionListItem.js';
const mapStateToProps = (state, ownProps) => { const mapStateToProps = (state, ownProps) => {
const { item } = ownProps; const { item } = ownProps;
@@ -27,5 +27,5 @@ const mapDispatchToProps = (dispatch) => ({
export default connect( export default connect(
mapStateToProps, mapStateToProps,
null, mapDispatchToProps,
)(AuctionListItem); )(AuctionListItem);

View File

@@ -5,8 +5,8 @@ import { StyleSheet, TouchableOpacity, Text, Image, View } from 'react-native';
import GallerySwiper from 'react-native-gallery-swiper'; import GallerySwiper from 'react-native-gallery-swiper';
import AuctionPriceAndBidCount from '../../containers/Auction/AuctionPriceAndBidCount.js'; import AuctionPriceAndBidCount from './AuctionPriceAndBidCount.container.js';
import BidStatus from '../../containers/Auction/BidStatus.js'; import BidStatus from './BidStatus.container.js';
import { ITEM_TYPES } from '../../constants/constants.js'; import { ITEM_TYPES } from '../../constants/constants.js';
import { formatPrice, getAuctionTime } from '../../library/helpers.js'; import { formatPrice, getAuctionTime } from '../../library/helpers.js';
@@ -83,28 +83,28 @@ export default class AuctionListItem extends Component {
)} )}
<View style={styles.rowText}> <View style={styles.rowText}>
{type === ITEM_TYPES.AUCTION && <BidStatus itemId={id} />} {type === ITEM_TYPES.AUCTION && <BidStatus itemId={id} />}
<Text style={styles.title} numberOfLines={2} ellipsizeMode={'tail'}> <Text style={styles.title} numberOfLines={2} ellipsizeMode="tail">
{title} {title}
</Text> </Text>
<Text style={styles.subtitle} numberOfLines={1} ellipsizeMode={'tail'}> <Text style={styles.subtitle} numberOfLines={1} ellipsizeMode="tail">
{subtitle} {subtitle}
</Text> </Text>
{donor && ( {donor && (
<Text style={styles.donor} numberOfLines={1} ellipsizeMode={'tail'}> <Text style={styles.donor} numberOfLines={1} ellipsizeMode="tail">
{donor} {donor}
</Text> </Text>
)} )}
{type === ITEM_TYPES.AUCTION ? ( {type === ITEM_TYPES.AUCTION ? (
<AuctionPriceAndBidCount itemId={id} /> <AuctionPriceAndBidCount itemId={id} />
) : ( ) : (
<Text style={styles.price} numberOfLines={1} ellipsizeMode={'tail'}> <Text style={styles.price} numberOfLines={1} ellipsizeMode="tail">
{formatPrice(startingPrice)} {formatPrice(startingPrice)}
</Text> </Text>
)} )}
<Text style={styles.timeline} numberOfLines={1}> <Text style={styles.timeline} numberOfLines={1}>
{this._getBidTime()} {this._getBidTime()}
</Text> </Text>
<Text style={styles.description} numberOfLines={3} ellipsizeMode={'tail'}> <Text style={styles.description} numberOfLines={3} ellipsizeMode="tail">
{description} {description}
</Text> </Text>
</View> </View>

View File

@@ -2,7 +2,7 @@ import { connect } from 'react-redux';
import { getItemBidCount, getItemPrice } from '../../selectors/auctions.js'; import { getItemBidCount, getItemPrice } from '../../selectors/auctions.js';
import AuctionPriceAndBidCount from '../../components/Auction/AuctionPriceAndBidCount.js'; import AuctionPriceAndBidCount from './AuctionPriceAndBidCount.js';
function mapStateToProps(state, ownProps) { function mapStateToProps(state, ownProps) {
const { itemId } = ownProps; const { itemId } = ownProps;

View File

@@ -6,9 +6,11 @@ import { formatPrice } from '../../library/helpers.js';
import { StyleSheet, Text } from 'react-native'; import { StyleSheet, Text } from 'react-native';
const AuctionPriceAndBidCount = ({ bidCount, currentPrice }) => { const AuctionPriceAndBidCount = ({ bidCount, currentPrice }) => {
const _getPriceAndBidString = () => `${formatPrice(currentPrice)} (${bidCount} bids)`;
return ( return (
<Text style={styles.currentPriceAndBidCount} numberOfLines={1}> <Text style={styles.currentPriceAndBidCount} numberOfLines={1}>
{`${formatPrice(currentPrice)} (${bidCount} bids)`} {_getPriceAndBidString()}
</Text> </Text>
); );
}; };

View File

@@ -2,7 +2,7 @@ import { connect } from 'react-redux';
import { isBiddingItem, isWinningItem } from '../../selectors/auctions.js'; import { isBiddingItem, isWinningItem } from '../../selectors/auctions.js';
import AuctionPriceAndBidCount from '../../components/Auction/BidStatus.js'; import AuctionPriceAndBidCount from './BidStatus.js';
function mapStateToProps(state, ownProps) { function mapStateToProps(state, ownProps) {
const { itemId } = ownProps; const { itemId } = ownProps;

View File

@@ -9,7 +9,7 @@ const BidStatus = ({ isBidding, isWinning }) => {
} }
const statusBarStyle = isWinning const statusBarStyle = isWinning
? [styles.bidStatus, styes.isWinning] ? [styles.bidStatus, styles.isWinning]
: [styles.bidStatus, styles.isOutbid]; : [styles.bidStatus, styles.isOutbid];
return ( return (

View File

@@ -3,10 +3,15 @@ import PropTypes from 'prop-types';
import { StyleSheet, Text, View } from 'react-native'; import { StyleSheet, Text, View } from 'react-native';
const STRINGS = {
FILTER: 'Filter',
VIEW: 'View',
};
const FilterBar = ({ changeFilterer, changeViewMode, filterMode, viewMode }) => ( const FilterBar = ({ changeFilterer, changeViewMode, filterMode, viewMode }) => (
<View style={styles.filterBar}> <View style={styles.filterBar}>
<Text style={styles.filter}>Filter</Text> <Text style={styles.filter}>{STRINGS.FILTER}</Text>
<Text style={styles.view}>View</Text> <Text style={styles.view}>{STRINGS.VIEW}</Text>
</View> </View>
); );

View File

@@ -1,7 +1,7 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { setActiveEvent } from '../../actions/events.js'; import { setActiveEvent } from '../../actions/events.js';
import EventListItem from '../../components/Events/EventListItem.js'; import EventListItem from './EventListItem.js';
const mapStateToProps = (state, ownProps) => { const mapStateToProps = (state, ownProps) => {
const { event } = ownProps; const { event } = ownProps;
@@ -27,5 +27,5 @@ const mapDispatchToProps = (dispatch) => ({
export default connect( export default connect(
mapStateToProps, mapStateToProps,
null, mapDispatchToProps,
)(AuctionListItem); )(EventListItem);

View File

@@ -33,24 +33,28 @@ export default class EventListItem extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this._viewEventDetail = this._viewEventDetail.bind(this);
} }
_viewEventDetail = () => { getTimeString() {
const { end, start } = this.props;
return `${start} - ${end}`;
}
_viewEventDetail() {
this.props.setActiveEvent(this.props.id); this.props.setActiveEvent(this.props.id);
this.props.navigation.navigate('Event'); this.props.navigation.navigate('Event');
}; }
render() { render() {
const { date, description, end, name, start } = this.props; const { date, description, name } = this.props;
return ( return (
<TouchableOpacity onPress={this._viewEventDetail}> <TouchableOpacity onPress={this._viewEventDetail}>
<View style={styles.rowContainer}> <View style={styles.rowContainer}>
<Text>{name}</Text> <Text>{name}</Text>
<Text>{date}</Text> <Text>{date}</Text>
<Text> <Text>{this.getTimeString()}</Text>
{start} - {end}
</Text>
<Text>{description}</Text> <Text>{description}</Text>
</View> </View>
</TouchableOpacity> </TouchableOpacity>

View File

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

View File

@@ -0,0 +1,8 @@
import { StyleSheet } from 'react-native';
export default StyleSheet.create({
container: {
flex: 1,
},
textInput: {},
});

View File

@@ -0,0 +1,225 @@
import { List } from 'immutable';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Button, Picker, ScrollView, Text, TextInput, View } from 'react-native';
import { Avatar } from 'react-native-elements';
import { getEmailAvailability, getNomAvailability } from '../../api/profile.js';
import EditNomDeBid from './ProfileInputs/EditNomDeBid.js';
import EmailInput from './ProfileInputs/EmailInput.js';
import PasswordInput from './ProfileInputs/PasswordInput.js';
import PhoneListInput from './ProfileInputs/PhoneListInput.js';
import styles from './Profile.styles.js';
const STRINGS = {
BUTTONS: {
CANCEL: 'Cancel',
SUBMIT: 'Register',
},
DEV: {
FORM_INCOMPLETE_SUBMIT: 'Incomplete form... how did the button become enabled?',
},
ERRORS: {
FORM_SUBMIT_ERRORS: 'Please complete all of the required fields. They have bold labels.',
},
HEADINGS: {
AVATAR: 'Want to add a picture?',
EMAIL: 'Email (this will be your username)',
NOM: 'and a Nom de Bid - your bidding alias!',
PASSWORD: 'For security, let\'s choose a password',
PERSONAL: 'Great! And now a bit about you...',
},
};
export default class CreateProfile extends Component {
static get propTypes() {
return {
cancelEditAction: PropTypes.func.isRequired,
saveProfileAction: PropTypes.func.isRequired,
};
}
constructor(props) {
super(props);
this.state = {
addresses: new List(),
avatar: null,
email: null,
firstName: null,
lastName: null,
invalidEmail: null,
nomDeBid: null,
password: null,
passwordMatch: false,
phones: new List(),
};
this.handleCancel = this.handleCancel.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this._handleValidPasswordEntry = this._handleValidPasswordEntry.bind(this);
}
_handleComponentStateUpdate(key, value) {
this.setState({ [key]: value });
}
_handleValidPasswordEntry(password) {
if (!password) {
this.setState({ passwordMatch: false });
return;
}
this.setState({ password, passwordMatch: true });
}
_validateEmail() {
getEmailAvailability(this.state.email)
.then((result) => {
console.log(`_validateEmail => getEmailAvailability(${this.state.email}):`, result);
this.setState({ invalidEmail: !result.available });
});
}
getProfileFromState() {
return {
addresses: this.state.addresses.toArray(),
avatar: this.state.avatar,
email: this.state.email,
firstName: this.state.firstName,
lastName: this.state.lastName,
nomDeBid: this.state.nomDeBid,
password: this.state.password,
phones: this.state.phones.toArray(),
};
}
handleCancel() {
this.props.cancelEditAction();
}
handleSubmit() {
if (!this.isFormComplete()) {
console.error(STRINGS.DEV.FORM_INCOMPLETE_SUBMIT);
alert(STRINGS.ERRORS.FORM_SUBMIT_ERRORS);
return;
}
this.props.saveProfileAction(this.getProfileFromState());
}
isFormComplete() {
return (
!!this.state.email &&
!!this.state.firstName &&
!!this.state.lastName &&
!!this.state.email &&
!!this.state.nomDeBid &&
!!this.state.phones.size &&
this.state.passwordMatch && !!this.state.password
);
}
render() {
const {
addresses,
avatar,
email,
firstName,
lastName,
nomDeBid,
password,
passwordMatch,
phones
} = this.state;
return (
<ScrollView style={styles.profileFormWrap}>
<View style={[styles.sectionWrap, styles.emailWrap, styles.requiredWrap]}>
<Text style={styles.groupHeading}>{STRINGS.HEADINGS.EMAIL}</Text>
<EmailInput
handleValidEmailEntry={(email) => this.setState({ email })}
isRequired
showContinueButton
/>
{email !== false && (
<Text style={{ color:'green', textAlign: 'center' }}>
{`Great, lets add a bit more detail...`}
</Text>
)}
</View>
{email !== null && (
<View style={[styles.sectionWrap, styles.nameWrap]}>
<Text style={styles.groupHeading}>{STRINGS.HEADINGS.PERSONAL}</Text>
<TextInput
onChange={(text) => this.setState({ firstName: text })}
placeholder="first name"
style={[styles.textInput, styles.requiredInput]}
value={this.state.firstName || ''}
/>
<TextInput
onChange={(text) => this.setState({ lastName: text })}
placeholder="last name"
style={[styles.textInput, styles.requiredInput]}
value={this.state.lastName || ''}
/>
</View>
)}
{firstName !== null && lastName !== null && (
<View style={[styles.sectionWrap, styles.password, styles.requiredWrap]}>
<Text style={styles.groupHeading}>{STRINGS.HEADINGS.PASSWORD}</Text>
<PasswordInput handleValidPasswordEntry={this._handleValidPasswordEntry} />
</View>
)}
{passwordMatch && (
<View style={[styles.sectionWrap, styles.nomWrap, styles.requiredWrap]}>
<Text style={styles.groupHeading}>{STRINGS.HEADINGS.NOM}</Text>
<EditNomDeBid
isStandalone
nomDeBid={this.state.nomDeBid}
updateNomDeBid={(nomDeBid) => this.setState({ nomDeBid })}
/>
</View>
)}
{nomDeBid !== null && (
<View style={styles.rollDownPanel}>
<View style={[styles.sectionWrap, styles.phonesWrap]}>
<PhoneListInput
handleAdd={(phones) => this.setState({ phones })}
handleDelete={(phones) => this.setState({ phones })}
handleEdit={(phones) => this.setState({ phones })}
phones={phones}
/>
</View>
<View style={[styles.sectionWrap, styles.addressesWrap]}>
{addresses !== null && addresses.size > 0 && (
/* LIST ADDRESSES */
<View />
)}
<Button title="Add address" onPress={() => false} />
</View>
<View style={[styles.sectionWrap, styles.avatarWrap]}>
<Text style={styles.groupHeading}>{STRINGS.AVATAR_HEADING}</Text>
{avatar !== null ? (
<Avatar source={{ uri: this.state.avatar }} showEditButton />
) : (
<Avatar title={this.props.initials} showEditButton />
)}
</View>
</View>
)}
{phones.size > 0 && (
<View style={styles.register}>
<Button title={STRINGS.BUTTONS.SUBMIT} onPress={this.handleSubmit} />
</View>
)}
<View style={styles.cancelWrap}>
<Button title={STRINGS.BUTTONS.CANCEL} onPress={this.handleCancel} />
</View>
</ScrollView>
);
}
}

View File

@@ -0,0 +1,21 @@
import { connect } from 'react-redux';
import { getProfile } from '../../selectors/profile.js';
import { matchStateToProps as matchCommonStateProps } from './Profile.stateProps.js';
import EditProfile from './EditProfile.js';
const matchStateToProps = (state) => {
const commonProps = matchCommonStateProps(state);
const profile = getProfile(state);
return {
...commonProps,
firstName: profile.get('firstName'),
lastName: profile.get('lastName'),
};
};
export default connect(
matchStateToProps,
null,
)(EditProfile);

View File

@@ -0,0 +1,234 @@
import { List } from 'immutable';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Button, Picker, ScrollView, Text, TextInput, View } from 'react-native';
import { Avatar } from 'react-native-elements';
import { getEmailAvailability, getNomAvailability } from '../../api/profile.js';
import EditNomDeBid from './ProfileInputs/EditNomDeBid.js';
import PasswordInput from './ProfileInputs/PasswordInput.js';
import PhoneListInput from './ProfileInputs/PhoneListInput.js';
import styles from './Profile.styles.js';
const STRINGS = {
BUTTONS: {
CANCEL: 'Cancel',
SUBMIT: 'Save changes',
},
DEV: {
FORM_INCOMPLETE_SUBMIT: 'Incomplete form... how did the button become enabled?',
},
ERRORS: {
FORM_SUBMIT_ERRORS: 'Please complete all of the required fields. They have bold labels.',
},
HEADINGS: {
NOM: 'Nom de Bid',
PASSWORD: 'Password',
},
};
export default class EditProfile extends Component {
static get propTypes() {
return {
addresses: PropTypes.oneOfType([PropTypes.array, PropTypes.instanceOf(List)]),
avatar: PropTypes.string,
cancelEditAction: PropTypes.func.isRequired,
email: PropTypes.string,
firstName: PropTypes.string,
hasLocalAccount: PropTypes.bool,
initials: PropTypes.string,
isGeneratedNomDeBid: PropTypes.bool,
lastName: PropTypes.string,
nomDeBid: PropTypes.string,
phones: PropTypes.oneOfType([PropTypes.array, PropTypes.instanceOf(List)]),
saveProfileAction: PropTypes.func.isRequired,
showPasswordEntry: PropTypes.bool,
};
}
static get defaultProps() {
return {
addresses: new List(),
avatar: null,
email: null,
firstName: null,
hasLocalAccount: false,
initials: null,
isGeneratedNomDeBid: false,
lastName: null,
nomDeBid: null,
phones: new List(),
showPasswordEntry: false,
};
}
constructor(props) {
super(props);
this.state = {
addresses: this.props.addresses,
avatar: this.props.avatar,
email: this.props.email,
firstName: this.props.firstName,
lastName: this.props.lastName,
invalidEmail: null,
nomDeBid: this.props.nomDeBid,
password: this.props.password,
passwordMatch: false,
phones: this.props.phones,
};
this.handleCancel = this.handleCancel.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
_handleValidPasswordEntry(password) {
if (!password) {
this.setState({ passwordMatch: false });
return;
}
this.setState({ password, passwordMatch: true });
}
_validateEmail() {
getEmailAvailability(this.state.email)
.then((result) => {
console.log(`_validateEmail => getEmailAvailability(${this.state.email}):`, result);
this.setState({ invalidEmail: !result.available });
});
}
getProfileFromState() {
return {
addresses: this.state.addresses.toArray(),
avatar: this.state.avatar,
email: this.state.email,
firstName: this.state.firstName,
lastName: this.state.lastName,
nomDeBid: this.state.nomDeBid,
password: this.state.password,
phones: this.state.phones.toArray(),
};
}
handleCancel() {
this.props.cancelEditAction();
}
handleSubmit() {
if (!this.isFormComplete()) {
console.error(STRINGS.DEV.FORM_INCOMPLETE_SUBMIT);
alert(STRINGS.ERRORS.FORM_SUBMIT_ERRORS);
return;
}
this.props.saveProfileAction(this.getProfileFromState());
}
isFormComplete() {
const { showPasswordEntry } = this.props;
const { password, passwordCheck } = this.state;
return (
!this.state.invalidEmail &&
!this.state.invalidNomDeBid &&
!!this.state.firstName &&
!!this.state.lastName &&
!!this.state.email &&
!!this.state.nomDeBid &&
!!this.state.phones.size &&
((showPasswordEntry && EditProfile.validatePasswordMatch(password, passwordCheck)) || !showPasswordEntry)
);
}
render() {
const { hasLocalAccount, isGeneratedNomDeBid, showPasswordEntry } = this.props;
const { addresses, avatar, firstName, invalidEmail, lastName, password, passwordCheck, phones } = this.state;
return (
<ScrollView style={styles.profileFormWrap}>
<View style={[styles.sectionWrap, styles.avatarWrap]}>
{avatar !== null ? (
<Avatar source={{ uri: this.state.avatar }} showEditButton />
) : (
<Avatar title={this.props.initials} showEditButton />
)}
</View>
<View style={[styles.sectionWrap, styles.emailWrap, styles.requiredWrap]}>
<TextInput
autoCapitalize="none"
keyboardType="email-address"
onChangeText={(text) => this.setState({ email: text })}
onEndEditing={(text) => this._validateEmail(text)}
placeholder="email address"
style={[styles.textInput, styles.requiredInput]}
value={this.state.email || ''}
/>
{invalidEmail === true && (
<View style={styles.emailTaken}>
<Text style={{color:'red'}}>{`You've already registered!`}</Text>
<Button title="Forgot Password" onPress={() => {}} />
</View>
)}
{invalidEmail === false && (
<Text style={{color:'green'}}>{`Great, lets add a bit more detail...`}</Text>
)}
</View>
<View style={[styles.sectionWrap, styles.nameWrap]}>
<TextInput
onChange={(text) => this.setState({ firstName: text })}
placeholder="first name"
style={[styles.textInput, styles.requiredInput]}
value={this.state.firstName || ''}
/>
<TextInput
onChange={(text) => this.setState({ lastName: text })}
placeholder="last name"
style={[styles.textInput, styles.requiredInput]}
value={this.state.lastName || ''}
/>
</View>
{showPasswordEntry && (
<View style={[styles.sectionWrap, styles.password, styles.requiredWrap]}>
<Text style={styles.groupHeading}>{STRINGS.HEADINGS.PASSWORD}</Text>
<PasswordInput handleValidPasswordEntry={this._handleValidPasswordEntry} />
</View>
)}
{(isGeneratedNomDeBid || !hasLocalAccount) && (
<View style={[styles.sectionWrap, styles.nomWrap, styles.requiredWrap]}>
<Text style={styles.groupHeading}>{STRINGS.HEADINGS.NOM}</Text>
<EditNomDeBid
isGeneratedNomDeBid={isGeneratedNomDeBid}
isStandalone
nomDeBid={this.state.nomDeBid}
updateNomDeBid={(nomDeBid) => this.setState({ nomDeBid })}
/>
</View>
)}
<View style={[styles.sectionWrap, styles.phonesWrap]}>
<PhoneListInput
handleAdd={(phones) => this.setState({ phones })}
handleDelete={(phones) => this.setState({ phones })}
handleEdit={(phones) => this.setState({ phones })}
phones={phones}
/>
</View>
<View style={[styles.sectionWrap, styles.addressesWrap]}>
{addresses !== null && addresses.size > 0 && (
/* LIST ADDRESSES */
<View />
)}
<Button title="Add address" onPress={() => false} />
</View>
<View style={styles.register}>
<Button title={STRINGS.BUTTONS.SUBMIT} onPress={this.handleSubmit} />
<Button title={STRINGS.BUTTONS.CANCEL} onPress={this.handleCancel} />
</View>
</ScrollView>
);
}
}

View File

@@ -0,0 +1,9 @@
import { connect } from 'react-redux';
import { matchStateToProps } from './Profile.stateProps.js';
import Profile from './Profile.js';
export default connect(
matchStateToProps,
null,
)(Profile);

View File

@@ -0,0 +1,59 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { View } from 'react-native';
import CreateProfile from './CreateProfile.js';
import EditProfile from './EditProfile.container.js';
import ViewProfile from './ViewProfile.container.js';
export default function Profile({
cancelEditAction,
isGuidedRegistration,
isInEditMode,
saveProfileAction,
}) {
const [editMode, setEditMode] = useState(isInEditMode);
const _cancelEditAction = () => {
setEditMode(false);
cancelEditAction();
};
const _saveProfileAction = (profile) => {
setEditMode(false);
saveProfileAction(profile);
};
return (
<View>
{!editMode && (
<ViewProfile editProfileAction={() => setEditMode(true)} />
)}
{editMode && isGuidedRegistration && (
<CreateProfile
cancelEditAction={_cancelEditAction}
saveProfileAction={_saveProfileAction}
/>
)}
{editMode && !isGuidedRegistration && (
<EditProfile
cancelEditAction={_cancelEditAction}
saveProfileAction={_saveProfileAction}
/>
)}
</View>
);
}
Profile.propTypes = {
cancelEditAction: PropTypes.func.isRequired,
isGuidedRegistration: PropTypes.bool,
isInEditMode: PropTypes.bool,
saveProfileAction: PropTypes.func.isRequired,
};
Profile.defaultProps = {
isGuidedRegistration: false,
isInEditMode: false,
saveProfileLabel: null,
};

View File

@@ -0,0 +1,16 @@
import { getProfile, isGeneratedNomDeBid, isRegisteredAccount } from '../../selectors/profile.js';
export const matchStateToProps = (state) => {
const profile = getProfile(state);
return {
addresses: profile.get('addresses'),
avatar: profile.get('avatar'),
email: profile.get('email'),
initials: profile.get('initials'),
isGeneratedNomDeBid: isGeneratedNomDeBid(state),
isRegisteredAccount: isRegisteredAccount(state),
nomDeBid: profile.get('nomDeBid'),
phones: profile.get('phones'),
};
};

View File

@@ -0,0 +1,36 @@
import { StyleSheet } from 'react-native';
export default StyleSheet.create({
container: {
alignItems: 'flex-start',
backgroundColor: '#F5FCFF',
flex: 1,
justifyContent: 'center',
paddingLeft: 10,
paddingRight: 10,
},
sectionWrap: {
marginBottom: 15,
},
emailWrap: {
marginBottom: 40,
marginTop: 20,
textAlign: 'center',
},
groupHeading: {
fontSize: 18,
fontWeight: 'bold',
margin: 10,
textAlign: 'center',
},
hintText: {
color: '#666666',
fontSize: 14,
margin: 5,
},
textInput: {
fontSize: 24,
margin: 10,
textAlign: 'center',
},
});

View File

@@ -0,0 +1,20 @@
import { connect } from 'react-redux';
import { setNomDeBid } from '../../../actions/profile.js';
import { getNomDeBid, isGeneratedNomDeBid } from '../../../selectors/profile.js';
import EditNomDeBid from './EditNomDeBid.js';
const matchStateToProps = (state) => ({
isGeneratedNomDeBid: isGeneratedNomDeBid(state),
nomDeBid: getNomDeBid(state),
});
const matchDispatchToProps = (dispatch) => ({
updateNomDeBid: (nomDeBid) => dispatch(setNomDeBid(nomDeBid)),
});
export default connect(
matchStateToProps,
matchDispatchToProps,
)(EditNomDeBid);

View File

@@ -0,0 +1,79 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { Button, Text, TextInput, View } from 'react-native';
import { Avatar } from 'react-native-elements';
import { getNomAvailaibility } from '../../../api/profile.js';
import styles from '../Profile.styles.js';
const STRINGS = {
ABOUT_GENERATED_NOM:
'You currently have a generated Nom De Bid - the alias other bidders will know you as - why not personalize it?',
CHECK_AVAILABILITY: 'check availability',
NOM_EXPLANATION:
"Selecting a nom de bid allows you to bid anonymously - or not. By default, we'll use your first initial and last name.",
ONLY_SET_ONCE: 'This can only be set once!',
PLACEHOLDER: 'nom de bid',
SUBMIT_NOM: 'Set Nom De Bid',
};
export default function EditNomDeBid({
isGeneratedNomDeBid,
isStandalone,
nomDeBid,
updateNomDeBid,
}) {
const [newNom, setNomDeBid] = useState(isGeneratedNomDeBid || !nomDeBid ? '' : nomDeBid);
const [isNomValid, setValidNom] = useState(null);
const _handleEndEditing = (updateOnValid = false) => () => {
getNomAvailaibility(newNom).then((result) => {
setValidNom(result.available);
if (updateOnValid && result.available) {
updateNomDeBid(newNom);
}
});
};
const explanationString = isGeneratedNomDeBid
? `${STRINGS.ABOUT_GENERATED_NOM} ${STRINGS.ONLY_SET_ONCE}`
: `${STRINGS.NOM_EXPLANATION} ${STRINGS.ONLY_SET_ONCE}`;
return (
<View style={styles.profileFormWrap}>
<Text style={styles.hintText}>{explanationString}</Text>
<TextInput
autoCapitalize="none"
onChangeText={(text) => setNomDeBid(text)}
onEndEditing={!isStandalone && _handleEndEditing(false)}
placeholder={STRINGS.PLACEHOLDER}
style={[styles.textInput, styles.requiredInput]}
value={newNom}
/>
{isNomValid === false && (
<Text style={{color:'red'}}>Nom De Bid is taken!</Text>
)}
{isNomValid === true && (
<Text style={{color:'green'}}>Nom De Bid is available!</Text>
)}
<Button
title={isStandalone ? STRINGS.CHECK_AVAILABILITY : STRINGS.SUBMIT_NOM}
onPress={_handleEndEditing(true)}
/>
</View>
);
}
EditNomDeBid.propTypes = {
isGeneratedNomDeBid: PropTypes.bool,
isStandalone: PropTypes.bool,
nomDeBid: PropTypes.string,
updateNomDeBid: PropTypes.func.isRequired,
};
EditNomDeBid.defaultProps = {
isGeneratedNomDeBid: false,
isStandalone: false,
nomDeBid: null,
};

View File

@@ -0,0 +1,86 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { Button, Text, TextInput, View } from 'react-native';
import { getEmailAvailability } from '../../../api/profile.js';
import styles from '../Profile.styles.js';
const STRINGS = {
CONTINUE: 'continue',
PLACEHOLDER: 'email address',
REGISTERED: `You've already registered!`,
RESET_PASSWORD: 'reset password',
};
export default function EmailInput({ email, handleValidEmailEntry, isRequired, showContinueButton }) {
const [emailValue, setEmail] = useState(email);
const [isEmailAvailable, setEmailAvailable] = useState(null);
const _handleContinueButtonPress = () => {
};
const _handleEndEditing = () => {
if (!emailValue.match(/.+\@.+\..+/)) {
Alert.alert(
'Invalid Email',
`Hmmm... You entered '${emailValue}' and something doesn't look quite right...`,
);
return;
}
getEmailAvailability(emailValue)
.then((result) => {
const { available } = result;
console.log(`EmailInput._validateEmail => getEmailAvailability(${emailValue}):`, result);
if (available) {
handleValidEmailEntry(emailValue);
}
setEmailAvailable(available);
});
};
return (
<View style={[styles.sectionWrap, styles.password, isRequired && styles.requiredWrap]}>
<TextInput
autoCapitalize="none"
keyboardType="email-address"
onChangeText={setEmail}
onEndEditing={showContinueButton ? null : _handleEndEditing}
placeholder={STRINGS.PLACEHOLDER}
style={[styles.textInput, isRequired && styles.requiredInput]}
value={emailValue || ''}
/>
{isEmailAvailable === false && (
<View style={styles.emailTaken}>
<Text style={{color:'red'}}>{STRINGS.REGISTERED}</Text>
<Button title={STRINGS.RESET_PASSWORD} onPress={() => {}} />
</View>
)}
{showContinueButton && !isEmailAvailable && (
<Button
onPress={_handleEndEditing}
title={STRINGS.CONTINUE}
/>
)}
</View>
);
}
EmailInput.propTypes = {
email: PropTypes.string,
handleValidEmailEntry: PropTypes.func.isRequired,
isRequired: PropTypes.bool,
showContinueButton: PropTypes.bool,
};
EmailInput.defaultProps = {
email: null,
isRequired: false,
showContinueButton: false,
}

View File

@@ -0,0 +1,53 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { Text, TextInput, View } from 'react-native';
import styles from '../Profile.styles.js';
const STRINGS = {
HINT: `Password must be at least 8 characters and contain a minimum of one lowercase and one uppercase letter, a number, and a symbol. Spaces are allowed. Be memorable and secure!`,
MATCH: `That's a match!`,
NO_MATCH: `Well that's not a match...`,
PLACEHOLDER_PASSWORD: 'password',
PLACEHOLDER_VERIFY: 're-enter password',
};
export default function PasswordInput({ handleValidPasswordEntry }) {
const [password, setPassword] = useState(null);
const [doPasswordsMatch, setPasswordMatch] = useState(null);
const _doesVerificationPasswordMatch = (text) => {
if (!password || password.length < text.length) {
setPasswordMatch(null);
return;
}
const result = password === text;
handleValidPasswordEntry(result && password);
setPasswordMatch(result);
};
return (
<View style={[styles.sectionWrap, styles.password, styles.requiredWrap]}>
<Text style={styles.hintText}>{STRINGS.HINT}</Text>
<TextInput
onChangeText={setPassword}
placeholder={STRINGS.PLACEHOLDER_PASSWORD}
secureTextEntry
style={[styles.textInput, styles.requiredInput]}
/>
<TextInput
onChangeText={_doesVerificationPasswordMatch}
placeholder={STRINGS.PLACEHOLDER_VERIFY}
secureTextEntry
style={[styles.textInput, styles.requiredInput]}
/>
{doPasswordsMatch === true && <Text style={{ color: 'green' }}>{STRINGS.MATCH}</Text>}
{doPasswordsMatch === false && <Text style={{ color: 'red' }}>{STRINGS.NO_MATCH}</Text>}
</View>
);
}
PasswordInput.propTypes = {
handleValidPasswordEntry: PropTypes.func.isRequired,
};

View File

@@ -0,0 +1,122 @@
import { List } from 'immutable';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { Button, Picker, Text, TextInput, View } from 'react-native';
import { PHONE_TYPE_DEFAULT, PHONE_TYPES } from '../../../constants/constants.js';
import PhoneListItem from './PhoneListItem.js';
import styles from '../Profile.styles.js';
const defaultState = {
editingIndex: null,
isEditing: false,
newPhone: null,
newPhoneType: PHONE_TYPE_DEFAULT,
};
export default class PhoneListInput extends Component {
static get propTypes() {
return {
handleAdd: PropTypes.func.isRequired,
handleDelete: PropTypes.func.isRequired,
handleEdit: PropTypes.func,
phones: PropTypes.instanceOf(List).isRequired,
};
};
static get defaultProps() {
return {
handleEdit: null,
};
};
constructor(props) {
super(props);
this.state = { ...defaultState };
this.handleAdd = this.handleAdd.bind(this);
this.handleEdit = this.handleEdit.bind(this);
this.handleEditCancel = this.handleEditCancel.bind(this);
}
handleAdd() {
const { phones } = this.props;
const { newPhone, newPhoneType } = this.state;
this.props.handleAdd(phones.push({ number: newPhone, label: newPhoneType }));
this.setState(defaultState);
}
handleEdit(index) {
const { phones } = this.props;
const { newPhone, newPhoneType } = this.state;
this.props.handleEdit(phones.set(index, { number: newPhone, label: newPhoneType }));
this.setState(defaultState);
}
handleEditStart(index) {
const toBeEdited = this.props.phones.get(index);
this.setState({
editingIndex: index,
isEditing: true,
newPhone: toBeEdited.get('number'),
newPhoneType: toBeEdited.get('label'),
});
}
handleEditCancel(index) {
this.setState(defaultState);
}
render() {
const { phones } = this.props;
const { isEditing, newPhone, newPhoneType } = this.state;
const numbersTitle = 'Numbers';
return (
<View>
<Text style={[styles.groupHeading, styles.requiredLabel]}>{numbersTitle}</Text>
{phones !== null && phones.size > 0 && (
<View style={styles.phoneList}>
{phones.map((phone, index) =>
<PhoneListItem
hideDelete={phones.size < 2}
index={index}
label={phone.label}
number={phone.number}
handleDelete={this.props.handleDelete}
handleEdit={this.props.handleEdit ? this.handleEdit : null}
handleEditStart={this.handleEditStart}
/>
)}
</View>
)}
<TextInput
onChangeText={(text) => this.setState({ newPhone: text })}
placeholder="phone number"
style={[styles.textInput, styles.requiredInput]}
value={this.state.newPhone}
/>
<Picker
onValueChange={(type, index) => this.setState({ newPhoneType: type })}
selectedValue={this.state.newPhoneType}
>
{PHONE_TYPES.map((type) => <Picker.Item key={type.value} {...type} />)}
</Picker>
<Button
disabled={!this.state.newPhone && !this.state.newPhoneType}
onPress={isEditing ? () => this.handleEdit(index) : this.handleAdd}
title={isEditing ? 'update' : 'add number'}
/>
{isEditing && (
<Button
onPress={this.handleEditCancel}
title="cancel"
/>
)}
</View>
);
}
}

View File

@@ -0,0 +1,76 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button } from 'react-native-elements';
import { Alert, Text, View } from 'react-native';
import styles from '../Profile.styles.js';
export default function PhoneListItem({
hideDelete,
index,
label,
number,
handleDelete,
handleEdit,
handleEditStart,
}) {
const _onLongPressDelete = () => {
handleDelete(index);
};
const _onPressDelete = (isLongPress = false) => () => {
if (isLongPress) {
handleDelete(index);
return;
}
Alert.alert(
'Delete this number?',
'Are you sure you want to delete this number?',
[
{ text: 'Cancel', style: 'cancel' },
{ text: 'OK', onPress: () => handleDelete(index) },
],
{ cancelable: true },
);
};
return (
<View style={styles.listItem}>
<View style={styles.listValue}>
<Text style={styles.value}>{number}</Text>
<Text style={styles.label}>{label}</Text>
</View>
<View style={styles.listActions}>
{handleEdit !== null && <Button accessibilityLabel={`Edit`} onPress={() => handleEditStart()} />}
{!hideDelete && (
<Button
accessibilityLabel={`Delete`}
onLongPress={_onPressDelete(true)}
onPress={_onPressDelete()}
/>
)}
</View>
</View>
);
}
PhoneListItem.propTypes = {
hideDelete: PropTypes.bool,
index: PropTypes.number.isRequired,
label: PropTypes.string.isRequired,
number: PropTypes.string.isRequired,
handleDelete: PropTypes.func.isRequired,
handleEdit: PropTypes.func,
handleEditStart: PropTypes.func.isRequired,
};
PhoneListItem.defaultProps = {
hideDelete: false,
handleEdit: null,
};

View File

@@ -0,0 +1,21 @@
import { connect } from 'react-redux';
import { getProfile } from '../../selectors/profile.js';
import { matchStateToProps as matchCommonStateProps } from './Profile.stateProps.js';
import ViewProfile from './ViewProfile.js';
const matchStateToProps = (state) => {
const commonProps = matchCommonStateProps(state);
const profile = getProfile(state);
return {
...commonProps,
fullName: profile.get('fullName'),
};
};
export default connect(
matchStateToProps,
null,
)(ViewProfile);

View File

@@ -0,0 +1,104 @@
import { List } from 'immutable';
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { Button, ScrollView, Text, TextInput, View } from 'react-native';
import { Avatar } from 'react-native-elements';
import EditNomDeBid from './ProfileInputs/EditNomDeBid.container.js';
import styles from './Profile.styles.js';
const STRINGS = {
EDIT: 'Edit profile',
};
export default function ViewProfile({
addresses,
avatar,
editProfileAction,
email,
fullName,
initials,
isGeneratedNomDeBid,
isRegisteredAccount,
nomDeBid,
phones,
}) {
const _getSavedText = (count) => count > 0 ? `${count} saved` : 'None saved';
const addressesCountString = _getSavedText(addresses.size);
const phonesCountString = _getSavedText(phones.size);
const [isEditingNom, setEditNom] = useState(false);
const addressesTitle = 'addresses';
const emailTitle = 'email';
const numbersTitle = 'numbers';
return (
<ScrollView style={styles.profileFormWrap}>
<View style={styles.avatarWrap}>
{avatar !== null ? (
<Avatar source={{ uri: avatar }} />
) : (
<Avatar title={initials} />
)}
</View>
<View style={styles.nameWrap}>
<Text style={styles.fullName}>{fullName}</Text>
<View style={styles.nomWrap}>
{isEditingNom ? (
<EditNomDeBid isStandalone />
) : (
<Text style={styles.nom}>{nomDeBid}</Text>
)}
{!isEditingNom && isGeneratedNomDeBid && isRegisteredAccount && (
<Button title="Set bidding alias" onPress={() => setEditNom(true)} />
)}
</View>
</View>
<View style={styles.emailWrap}>
<Text style={styles.label}>{emailTitle}</Text>
<Text style={styles.value}>{email}</Text>
</View>
<View style={styles.phonesWrap}>
<Text style={styles.label}>{numbersTitle}</Text>
<Text style={styles.value}>{phonesCountString}</Text>
</View>
<View style={styles.addressesWrap}>
<Text style={styles.label}>{addressesTitle}</Text>
<Text style={styles.value}>{addressesCountString}</Text>
</View>
{editProfileAction !== null && (
<View style={styles.register}>
<Button title={STRINGS.EDIT} onPress={editProfileAction} />
</View>
)}
</ScrollView>
);
}
ViewProfile.propTypes = {
addresses: PropTypes.oneOfType([PropTypes.array, PropTypes.instanceOf(List)]),
avatar: PropTypes.string,
editProfileAction: PropTypes.func,
email: PropTypes.string,
fullName: PropTypes.string,
initials: PropTypes.string,
isGeneratedNomDeBid: PropTypes.bool,
isRegisteredAccount: PropTypes.bool,
nomDeBid: PropTypes.string,
phones: PropTypes.oneOfType([PropTypes.array, PropTypes.instanceOf(List)]),
};
ViewProfile.defaultProps = {
addresses: new List(),
avatar: null,
editProfileAction: null,
email: null,
fullName: null,
initials: null,
isGeneratedNomDeBid: false,
isRegisteredAccount: false,
nomDeBid: null,
phones: new List(),
};

View File

@@ -50,6 +50,9 @@ 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';
export const SET_NOM_FAILURE = 'SET_NOM_FAILURE';
export const SET_NOM_SUCCESS = 'SET_NOM_SUCCESS';
export const SET_PASSWORD = 'SET_PASSWORD'; export const SET_PASSWORD = 'SET_PASSWORD';
export const ADD_PAYMENT_DATA = 'ADD_PAYMENT_DATA'; export const ADD_PAYMENT_DATA = 'ADD_PAYMENT_DATA';

View File

@@ -51,3 +51,11 @@ export const API_ENDPOINTS = {
export const PERMISSIONS = { export const PERMISSIONS = {
FACEBOOK: ['email', 'public_profile'], FACEBOOK: ['email', 'public_profile'],
}; };
export const PHONE_TYPES = [
{ label: 'home', value: 'home' },
{ label: 'mobile', value: 'mobile' },
{ label: 'work', value: 'work' },
];
export const PHONE_TYPE_DEFAULT = PHONE_TYPES[0].value;

View File

@@ -1,22 +0,0 @@
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

@@ -28,6 +28,17 @@ export default class Profile extends Record({
return `${this.firstName} ${this.lastName}`; return `${this.firstName} ${this.lastName}`;
} }
get initials() {
const firstInitial = this.firstName ? this.firstName.substring(0, 1) : null;
const lastInitial = this.firstName ? this.firstName.substring(0, 1) : null;
if (!firstInitial && !lastInitial) {
return null;
}
return `${firstInitial || ''}${lastInitial || ''}`;
}
get isRegisteredAccount() { get isRegisteredAccount() {
return ( return (
this.hasLinkedApple || this.hasLinkedApple ||

View File

@@ -13,9 +13,20 @@ import ImageDetail from './screens/ImageDetail.js';
import Item from './screens/Item.js'; import Item from './screens/Item.js';
import Marketplace from './screens/Marketplace.js'; import Marketplace from './screens/Marketplace.js';
import Profile from './screens/Profile.container.js'; import Profile from './screens/Profile.container.js';
import Register from './screens/Register.js'; import Register from './screens/Register.container.js';
import SignInOrRegister from './screens/SignInOrRegister.js'; import SignInOrRegister from './screens/SignInOrRegister.js';
const tabBarVisibility = ({ navigation }) => {
let tabBarVisible = true;
if (navigation.state.index > 0) {
tabBarVisible = false;
}
return {
tabBarVisible,
};
};
export const SignInOrRegisterStack = createStackNavigator({ export const SignInOrRegisterStack = createStackNavigator({
SignInOrRegister: { SignInOrRegister: {
screen: SignInOrRegister, screen: SignInOrRegister,
@@ -35,6 +46,33 @@ export const SignInOrRegisterStack = createStackNavigator({
}, },
}); });
SignInOrRegisterStack.navigationOptions = tabBarVisibility;
export const ProfileStack = createStackNavigator({
Profile: {
screen: Profile,
navigationOptions: ({ navigation }) => ({
header: <AppHeader navigation={navigation} />,
}),
},
SignInOrRegister: {
screen: SignInOrRegister,
navigationOptions: ({ navigation }) => ({
header: null,
gesturesEnabled: false,
}),
},
Register: {
screen: Register,
navigationOptions: ({ navigation }) => ({
header: null,
gesturesEnabled: false,
}),
},
});
ProfileStack.navigationOptions = tabBarVisibility;
export const AuctionStack = createStackNavigator({ export const AuctionStack = createStackNavigator({
Auction: { Auction: {
screen: Auction, screen: Auction,
@@ -60,6 +98,8 @@ export const AuctionStack = createStackNavigator({
}, },
}); });
AuctionStack.navigationOptions = tabBarVisibility;
export const BazaarStack = createStackNavigator({ export const BazaarStack = createStackNavigator({
Bazaar: { Bazaar: {
screen: Marketplace, screen: Marketplace,
@@ -93,12 +133,13 @@ export const BazaarStack = createStackNavigator({
}, },
}); });
BazaarStack.navigationOptions = tabBarVisibility;
export const EventsStack = createStackNavigator({ export const EventsStack = createStackNavigator({
Events: { Events: {
screen: Events, screen: Events,
navigationOptions: ({ navigation }) => ({ navigationOptions: ({ navigation }) => ({
header: <AppHeader navigation={navigation} />, header: <AppHeader navigation={navigation} />,
tabBarVisible: false,
gesturesEnabled: false, gesturesEnabled: false,
}), }),
}, },
@@ -106,12 +147,13 @@ export const EventsStack = createStackNavigator({
screen: Event, screen: Event,
navigationOptions: ({ navigation }) => ({ navigationOptions: ({ navigation }) => ({
header: <AppHeader navigation={navigation} />, header: <AppHeader navigation={navigation} />,
tabBarVisible: false,
gesturesEnabled: false, gesturesEnabled: false,
}), }),
}, },
}); });
EventsStack.navigationOptions = tabBarVisibility;
export const Tabs = createBottomTabNavigator({ export const Tabs = createBottomTabNavigator({
Event: { Event: {
screen: EventsStack, screen: EventsStack,
@@ -141,52 +183,12 @@ export const Tabs = createBottomTabNavigator({
}, },
}, },
Profile: { Profile: {
screen: Profile, screen: ProfileStack,
navigationOptions: { navigationOptions: {
tabBarLabel: 'Profile', tabBarLabel: 'Profile',
tabBarIcon: ({ tintColor }) => ( tabBarIcon: ({ tintColor }) => (
<Icon name="ios-person" type="font-awesome" size={28} color={tintColor} /> <Icon name="user" type="evilicon" size={28} color={tintColor} />
), ),
}, },
}, },
}); }, { initialRouteName: 'Event' });
export const createRootNavigator = () => {
return StackNavigator(
{
AuctionStack: {
screen: AuctionStack,
navigationOptions: {
gesturesEnabled: false,
},
},
BazaarStack: {
screen: BazaarStack,
navigationOptions: {
gesturesEnabled: false,
},
},
EventsStack: {
screen: EventsStack,
navigationOptions: {
gesturesEnabled: false,
},
},
SignInOrRegisterStack: {
screen: SignInOrRegister,
navigationOptions: {
gesturesEnabled: false,
},
},
Tabs: {
screen: Tabs,
navigationOptions: {
gesturesEnabled: false,
},
},
},
{
mode: 'modal',
},
);
};

View File

@@ -7,17 +7,16 @@ import { getAuctionItemsAsList } from '../selectors/items.js';
import Auction from './Auction.js'; import Auction from './Auction.js';
const matchStateToProps = (state) => { const changeViewMode = () => true;
const items = getAuctionItemsAsList(state);
console.log('items:', items);
return { items }; const matchStateToProps = (state) => ({
}; items: getAuctionItemsAsList(state),
});
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
changeViewMode: (mode) => dispatch(changeViewMode(mode)), changeViewMode: (mode) => dispatch(changeViewMode(mode)),
fetchItems: () => dispatch(fetchItems(dispatch)), fetchItems: () => dispatch(fetchItems()),
fetchStatus: () => dispatch(fetchAuctionStatus(dispatch)), fetchStatus: () => dispatch(fetchAuctionStatus()),
}); });
export default connect( export default connect(

View File

@@ -7,7 +7,7 @@ import { FlatList, Text, View } from 'react-native';
import { SORT_MODES, AUCTION_VIEW_MODES } from '../constants/constants.js'; import { SORT_MODES, AUCTION_VIEW_MODES } from '../constants/constants.js';
import FilterBar from '../components/Auction/FilterBar.js'; import FilterBar from '../components/Auction/FilterBar.js';
import AuctionListItem from '../containers/Auction/AuctionListItem.js'; import AuctionListItem from '../components/Auction/AuctionListItem.container.js';
import styles from './Auction.styles.js'; import styles from './Auction.styles.js';

View File

@@ -1,6 +1,6 @@
import { StyleSheet } from 'react-native'; import { StyleSheet } from 'react-native';
export default (styles = StyleSheet.create({ export default StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
justifyContent: 'center', justifyContent: 'center',
@@ -14,4 +14,4 @@ export default (styles = StyleSheet.create({
alignItems: 'stretch', alignItems: 'stretch',
justifyContent: 'flex-start', justifyContent: 'flex-start',
}, },
})); });

View File

@@ -3,9 +3,11 @@ import { StyleSheet, Text, View } from 'react-native';
export default class Checkout extends Component { export default class Checkout extends Component {
render() { render() {
const title = 'Checkout';
return ( return (
<View style={styles.container}> <View style={styles.container}>
<Text style={styles.title}>Checkout</Text> <Text style={styles.title}>{title}</Text>
</View> </View>
); );
} }

View File

@@ -73,7 +73,7 @@ export default class Event extends Component {
return ( return (
<View style={styles.container}> <View style={styles.container}>
<Text style={styles.title}>Event</Text> <Text style={styles.title}>{title}</Text>
</View> </View>
); );
} }

View File

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

View File

@@ -6,10 +6,9 @@ import { getEventsAsList } from '../selectors/events.js';
import Events from './Events.js'; import Events from './Events.js';
const matchStateToProps = (state) => { const matchStateToProps = (state) => ({
const events = getEventsAsList(state); events: getEventsAsList(state),
return { events }; });
};
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
fetchEvents: () => dispatch(fetchEvents()), fetchEvents: () => dispatch(fetchEvents()),

View File

@@ -2,14 +2,14 @@ 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 { FlatList, StyleSheet, Text, View } from 'react-native'; import { ActivityIndicator, FlatList, StyleSheet, View } from 'react-native';
import EventListItem from '../components/Events/EventListItem.js'; import EventListItem from '../components/Events/EventListItem.container.js';
export default class Events extends Component { export default class Events extends Component {
static get propTypes() { static get propTypes() {
return { return {
events: PropTypes.instanceOf(List), events: PropTypes.oneOfType([PropTypes.array, PropTypes.instanceOf(List)]),
fetchEvents: PropTypes.func.isRequired, fetchEvents: PropTypes.func.isRequired,
setActiveEvent: PropTypes.func.isRequired, setActiveEvent: PropTypes.func.isRequired,
}; };
@@ -17,36 +17,32 @@ export default class Events extends Component {
static get defaultProps() { static get defaultProps() {
return { return {
events: new List(), events: [],
}; };
} }
constructor(props) { constructor(props) {
super(props); super(props);
this._renderEventListItem = this._renderEventListItem.bind(this);
this._setActiveEvent = this.setActiveEvent.bind(this); this._setActiveEvent = this.setActiveEvent.bind(this);
} }
componentDidMount() {
this.props.fetchEvents();
}
setActiveEvent(eventId) { setActiveEvent(eventId) {
this.props.setActiveEvent(eventId); this.props.setActiveEvent(eventId);
} }
_keyExtractor = (event, index) => `${event.id}_${index}`; _keyExtractor = (event, index) => `${event.id}_${index}`;
_renderEventListItem = ({ event }) => ( _renderEventListItem({ event }) {
<EventListItem {...event} setActiveEvent={this.setActiveEvent} /> return <EventListItem event={event} setActiveEvent={this.setActiveEvent} />;
); };
render() { render() {
const { events } = this.props; const { events } = this.props;
return ( return (
<View style={styles.container}> <View style={styles.container}>
{events.size > 0 && ( {events.size > 0 ? (
<FlatList <FlatList
data={events} data={events}
keyExtractor={this._keyExtractor} keyExtractor={this._keyExtractor}
@@ -54,6 +50,8 @@ export default class Events extends Component {
contentContainerStyle={styles.eventListContentContainer} contentContainerStyle={styles.eventListContentContainer}
style={styles.eventList} style={styles.eventList}
/> />
) : (
<ActivityIndicator animating={true} size="large" />
)} )}
</View> </View>
); );
@@ -67,5 +65,8 @@ const styles = StyleSheet.create({
alignItems: 'center', alignItems: 'center',
backgroundColor: '#F5FCFF', backgroundColor: '#F5FCFF',
}, },
eventListContentContainer: {}, eventListContentContainer: {
alignItems: 'stretch',
justifyContent: 'flex-start',
},
}); });

View File

@@ -3,9 +3,11 @@ import { StyleSheet, Text, View } from 'react-native';
export default class ImageDetail extends Component { export default class ImageDetail extends Component {
render() { render() {
const title = 'Item';
return ( return (
<View style={styles.container}> <View style={styles.container}>
<Text style={styles.title}>Item</Text> <Text style={styles.title}>{title}</Text>
</View> </View>
); );
} }

View File

@@ -3,9 +3,11 @@ import { StyleSheet, Text, View } from 'react-native';
export default class Item extends Component { export default class Item extends Component {
render() { render() {
const title = 'Item';
return ( return (
<View style={styles.container}> <View style={styles.container}>
<Text style={styles.title}>Item</Text> <Text style={styles.title}>{title}</Text>
</View> </View>
); );
} }

View File

@@ -1,26 +0,0 @@
import React, { Component } from 'react';
import { StyleSheet, Text, View } from 'react-native';
export default class Login extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.title}>Login</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
title: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});

View File

@@ -7,9 +7,9 @@ import { FlatList, Text, View } from 'react-native';
import { SORT_MODES, AUCTION_VIEW_MODES } from '../constants/constants.js'; import { SORT_MODES, AUCTION_VIEW_MODES } from '../constants/constants.js';
import FilterBar from '../components/Auction/FilterBar.js'; import FilterBar from '../components/Auction/FilterBar.js';
import AuctionListItem from '../containers/Auction/AuctionListItem.js'; import AuctionListItem from '../components/Auction/AuctionListItem.container.js';
//import styles from './Marketplace.styles.js'; import styles from './Auction.styles.js';
export default class Marketplace extends Component { export default class Marketplace extends Component {
static get propTypes() { static get propTypes() {

35
app/screens/Modal.js Normal file
View File

@@ -0,0 +1,35 @@
import PropTypes from 'prop-types';
import React from 'react';
import { StyleSheet, Button, View } from 'react-native';
const Modal = ({ children, navigation, title }) => (
<View style={styles.container}>
<Button onPress={() => navigation.goBack()} title="Dismiss" />
{children}
</View>
);
Modal.propTypes = {
children: PropTypes.node.isRequired,
title: PropTypes.string,
};
Modal.defaultPorps = {
title: null,
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
title: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
export default Modal;

View File

@@ -1,7 +1,7 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { fetchProfile, updateProfile } from '../actions/profile.js'; import { fetchProfile, updateProfile } from '../actions/profile.js';
import { getNomDeBid, getProfile, isAllowedToBid } from '../selectors/profile.js'; import { getProfile, isAllowedToBid } from '../selectors/profile.js';
import Profile from './Profile.js'; import Profile from './Profile.js';
@@ -13,13 +13,8 @@ const matchStateToProps = (state) => {
hasLinkedFacebook: profile.get('hasLinkedFacebook'), hasLinkedFacebook: profile.get('hasLinkedFacebook'),
hasLinkedGoogle: profile.get('hasLinkedGoogle'), hasLinkedGoogle: profile.get('hasLinkedGoogle'),
hasLocalAccount: profile.get('hasLocalAccount'), hasLocalAccount: profile.get('hasLocalAccount'),
hasRegisteredAcccount: profile.get('hasRegisteredAcccount'),
id: profile.get('id'),
isAllowedToBid: isAllowedToBid(state), isAllowedToBid: isAllowedToBid(state),
isVerified: profile.get('isVerified'), isVerified: profile.get('isVerified'),
lastName: profile.get('lastName'),
nomDeBid: getNomDeBid(state),
organizationIdentifier: profile.get('organizationIdentifier'),
paymentToken: profile.get('paymentToken'), paymentToken: profile.get('paymentToken'),
}; };
}; };

View File

@@ -1,26 +1,95 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { StyleSheet, Text, View } from 'react-native'; import PropTypes from 'prop-types';
import { Text, View } from 'react-native';
import ProfileUtility from '../components/Profile/Profile.container.js';
import styles from './Profile.styles.js';
const STRINGS = {
EMAIL_NEEDS_VERIFICATION: 'Your acount has not been verified, please check your email.',
};
export default class Profile extends Component { export default class Profile extends Component {
static get propTypes() {
return {
hasLinkedApple: PropTypes.bool,
hasLinkedFacebook: PropTypes.bool,
hasLinkedGoogle: PropTypes.bool,
hasLocalAccount: PropTypes.bool,
isAllowedToBid: PropTypes.bool,
isVerified: PropTypes.bool,
paymentToken: PropTypes.string,
updateProfile: PropTypes.func.isRequired,
};
}
static get defaultProps() {
return {
hasLinkedApple: false,
hasLinkedFacebook: false,
hasLinkedGoogle: false,
hasLocalAccount: false,
isAllowedToBid: false,
isVerified: false,
paymentToken: null,
};
}
render() { render() {
const {
hasLinkedApple,
hasLinkedFacebook,
hasLinkedGoogle,
hasLocalAccount,
isAllowedToBid,
isVerified,
paymentToken,
updateProfile,
} = this.props;
return ( return (
<View style={styles.container}> <View style={styles.container}>
<Text style={styles.title}>Profile</Text> {!isVerified && (
<View style={styles.alertBar}>
<Text style={styles.alert}>{STRINGS.EMAIL_NEEDS_VERIFICATION}</Text>
</View>
)}
<ProfileUtility
cancelEditAction={() => false}
saveProfileAction={updateProfile}
saveProfileLabel="Update profile"
/>
{!isAllowedToBid ? (
/* ADD PAYMENT METHOD */
<View />
) : (
/* SHOW/EDIT PAYMENT METHOD */
<View />
)}
{!hasLocalAccount && (
/* CREATE LOCAL ACCOUNT PASSWORD CTA */
<View />
)}
{hasLinkedApple && (
/* APPLE LINK/UNLINK */
<View />
)}
{hasLinkedFacebook && (
/* FACEBOOK LINK/UNLINK */
<View />
)}
{hasLinkedGoogle && (
/* GOOGLE LINK/UNLINK */
<View />
)}
</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,15 @@
import { StyleSheet } from 'react-native';
export default StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
title: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});

View File

@@ -1,132 +1,34 @@
import React, { Component } from 'react'; import React from 'react';
import PropTypes from 'prop-types';
import { Text, View } from 'react-native'; import { Text, View } from 'react-native';
import CreateProfile from '../components/Profile/CreateProfile.js';
import styles from './Register.styles.js'; import styles from './Register.styles.js';
const STRINGS = { export default function Register({ doRegistration, navigation }) {
NOM_EXPLANATION: 'Selecting a nom de bid allows you to bid anonymously - or not. By default, we\'ll use your first initial and last name.', const title = 'Register';
SUBMIT_REGISTRATION: 'Register',
};
export default class Register extends Component { const _doRegistration = (profile) => {
static get propTypes() { if (!profile) {
return {
checkEmail: PropTypes.func.isRequired,
checkNomDeBid: PropTypes.func.isRequired,
doRegistration: PropTypes.func.isRequired,
// invalidEmail: PropTypes.bool.isRequired,
// invalidNomDeBid: PropTypes.bool.isRequired,
};
}
constructor() {
super(props);
this.state = {
addresses: [],
avatar: null,
email: null,
firstName: null,
lastName: null,
nomDeBid: null,
invalidEmail: false,
invalidNomDeBid: false,
password: null,
phones: [],
};
this._doRegistration = this._doRegistration.bind(this);
}
_doRegistration() {
if (!this.isFormComplete()) {
console.error('Incomplete form... how did the button become enabled?');
alert('Please complete all of the required fields. They have bold labels.');
return; return;
} }
this.props.doRegistration(this.getUserRegistration()); doRegistration(profile);
} };
_validateEmail() {
this.props.checkEmail(this.state.email, (valid) => this.setState('invalidEmail', valid));
}
_validateNomDeBid() {
this.props.checkNomDeBid(this.state.nomDeBid, (valid) =>
this.setState('invalidNomDeBid', valid),
);
}
getUserRegistration() {
return {
addresses: this.state.addresses,
avatar: this.state.avatar,
email: this.state.email,
firstName: this.state.firstName,
lastName: this.state.lastName,
nomDeBid: this.state.nomDeBid,
password: this.state.password,
phones: this.state.phones,
};
}
isFormComplete() {
return (
!this.state.invalidEmail &&
!this.state.invalidNomDeBid &&
!!this.state.firstName &&
!!this.state.lastName &&
!!this.state.email &&
!!this.state.nomDeBid &&
!!this.state.phones.length &&
!!this.state.password
);
}
render() {
return ( return (
<View style={styles.container}> <View style={styles.container}>
<Text style={styles.title}>Register</Text> <Text style={styles.heading}>{title}</Text>
<View style={styles.nameWrap}> <CreateProfile
<TextInput cancelEditAction={() => navigation.goBack()}
onChange={(text) => this.setState({ firstName: text })} saveProfileAction={_doRegistration}
placeholder="first name" showPasswordEntry
style={styles.textInput} />
value={this.state.firstName}
/>
<TextInput
onChange={(text) => this.setState({ lastName: text })}
placeholder="last name"
style={styles.textInput}
value={this.state.lastName}
/>
</View> </View>
<View style={styles.emailWrap}>
<TextInput
keyboardType="email-address"
onChangeText={(text) => _updateState('username', text)}
onEndEditing={(text) => this._validateEmail(text)}
placeholder="email address"
style={styles.textInput}
value={this.state.email}
/>
</View>
<View style={styles.nomWrap}>
<Text style={styles.hintText}>{STRINGS.NOM_EXPLANATION}</Text>
<TextInput
keyboardType="email-address"
onChangeText={(text) => _updateState('username', text)}
onEndEditing={(text) => this._validateEmail(text)}
placeholder="email address"
style={styles.textInput}
value={this.state.email}
/>
</View>
<View style={styles.register}>
<Button title={STRINGS.SUBMIT_REGISTRATION} onPress={this._doRegistration} />
</View>
</View>
); );
}
} }
Register.propTypes = {
doRegistration: PropTypes.func.isRequired,
};

View File

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

View File

@@ -1,37 +1,26 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Text, View } from 'react-native'; import { Button, Text, View } from 'react-native';
import { Button } from 'react-native-elements';
import FacebookLogin from '../components/Login/FacebookLogin.container.js'; import FacebookLogin from '../components/Login/FacebookLogin.container.js';
import LocalLogin from '../components/Login/LocalLogin.container.js'; import LocalLogin from '../components/Login/LocalLogin.container.js';
import styles from './SignInOrRegister.styles.js'; import styles from './SignInOrRegister.styles.js';
export default class SignInOrRegister extends Component { export default function SignInOrRegister({ navigation }) {
constructor() { const title = 'Sign In or Register';
super(props);
this._doRegistration = this._doRegistration.bind(this); return (
} <View style={styles.container}>
<Text style={styles.title}>{title}</Text>
_doRegistration() { <View style={styles.localLogin}>
this.props.navigation.navigate('Register'); <LocalLogin />
}
render() {
return (
<View style={styles.container}>
<Text style={styles.title}>Sign In or Register</Text>
<View style={styles.localLogin}>
<LocalLogin />
</View>
<View style={styles.services}>
<FacebookLogin />
</View>
<View style={styles.register}>
<Button title="Signup with Email" onPress={this._doRegistration} />
</View>
</View> </View>
); <View style={styles.services}>
} <FacebookLogin />
</View>
<View style={styles.register}>
<Button title="Signup with Email" onPress={() => navigation.navigate('Register')} />
</View>
</View>
);
} }

View File

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

View File

@@ -3,9 +3,11 @@ import { StyleSheet, Text, View } from 'react-native';
export default class Ticketing extends Component { export default class Ticketing extends Component {
render() { render() {
const title = 'Ticketing';
return ( return (
<View style={styles.container}> <View style={styles.container}>
<Text style={styles.title}>Ticketing</Text> <Text style={styles.title}>{title}</Text>
</View> </View>
); );
} }

View File

@@ -1,3 +1,4 @@
import { Map } from 'immutable';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
const getState = (state) => state; const getState = (state) => state;
@@ -16,7 +17,7 @@ export const getAuctionStatus = (state, itemId) => state.getIn(['actions', itemI
export const getAuctionStatuses = createSelector( export const getAuctionStatuses = createSelector(
[getState], [getState],
(state) => state.get('actions') || new Map(), (state) => state.get('autions') || new Map(),
); );
export const getItemsIdsWithNoBids = createSelector( export const getItemsIdsWithNoBids = createSelector(

View File

@@ -18,8 +18,8 @@ export const getActiveEvent = createSelector(
); );
export const getDefaultEvent = createSelector( export const getDefaultEvent = createSelector(
[getEvents], [getActiveEvent, getEvents],
(eventsAsMap) => eventsAsMap.first(), (activeEvent, eventsAsMap) => activeEvent || eventsAsMap.first(),
); );
export const getEventsAsList = createSelector( export const getEventsAsList = createSelector(

View File

@@ -18,7 +18,27 @@ export const getProfileAvatarUrl = createSelector(
(profile) => profile.get('avatar'), (profile) => profile.get('avatar'),
); );
export const getUserId = createSelector(
[getProfile],
(profile) => profile.get('id'),
);
export const getUserInitials = createSelector(
[getProfile],
(profile) => profile.get('initials'),
);
export const isAllowedToBid = createSelector( export const isAllowedToBid = createSelector(
[getProfile], [getProfile],
(profile) => profile.get('isAllowedToBid'), (profile) => profile.get('isAllowedToBid'),
); );
export const isGeneratedNomDeBid = createSelector(
[getProfile],
(profile) => profile.get('generatedNomDeBid'),
);
export const isRegisteredAccount = createSelector(
[getProfile],
(profile) => profile.get('isRegisteredAccount'),
);

View File

@@ -848,9 +848,43 @@
); );
inputPaths = ( inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Eventment/Pods-Eventment-resources.sh", "${PODS_ROOT}/Target Support Files/Pods-Eventment/Pods-Eventment-resources.sh",
"${PODS_ROOT}/FBSDKCoreKit/FacebookSDKStrings.bundle",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Feather.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Brands.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Regular.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Solid.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Fontisto.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Foundation.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Octicons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf",
); );
name = "[CP] Copy Pods Resources"; name = "[CP] Copy Pods Resources";
outputPaths = ( outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FacebookSDKStrings.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EvilIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Feather.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Brands.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Regular.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Solid.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Fontisto.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Foundation.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Ionicons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialCommunityIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Octicons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SimpleLineIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf",
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;

View File

@@ -1,6 +1,16 @@
PODS: PODS:
- boost-for-react-native (1.63.0) - boost-for-react-native (1.63.0)
- DoubleConversion (1.1.6) - DoubleConversion (1.1.6)
- FBSDKCoreKit (5.3.0):
- FBSDKCoreKit/Basics (= 5.3.0)
- FBSDKCoreKit/Core (= 5.3.0)
- FBSDKCoreKit/Basics (5.3.0)
- FBSDKCoreKit/Core (5.3.0):
- FBSDKCoreKit/Basics
- FBSDKLoginKit (5.3.0):
- FBSDKCoreKit (~> 5.0)
- FBSDKShareKit (5.3.0):
- FBSDKCoreKit (~> 5.0)
- Folly (2018.10.22.00): - Folly (2018.10.22.00):
- boost-for-react-native - boost-for-react-native
- DoubleConversion - DoubleConversion
@@ -57,6 +67,20 @@ PODS:
- React-cxxreact (= 0.60.0) - React-cxxreact (= 0.60.0)
- React-jsi (= 0.60.0) - React-jsi (= 0.60.0)
- React-jsinspector (0.60.0) - React-jsinspector (0.60.0)
- react-native-fbsdk (1.0.1):
- React
- react-native-fbsdk/Core (= 1.0.1)
- react-native-fbsdk/Login (= 1.0.1)
- react-native-fbsdk/Share (= 1.0.1)
- react-native-fbsdk/Core (1.0.1):
- FBSDKCoreKit (~> 5.0)
- React
- react-native-fbsdk/Login (1.0.1):
- FBSDKLoginKit (~> 5.0)
- React
- react-native-fbsdk/Share (1.0.1):
- FBSDKShareKit (~> 5.0)
- React
- React-RCTActionSheet (0.60.0): - React-RCTActionSheet (0.60.0):
- React-Core (= 0.60.0) - React-Core (= 0.60.0)
- React-RCTAnimation (0.60.0): - React-RCTAnimation (0.60.0):
@@ -81,10 +105,14 @@ PODS:
- React-RCTWebSocket (0.60.0): - React-RCTWebSocket (0.60.0):
- React-Core (= 0.60.0) - React-Core (= 0.60.0)
- React-fishhook (= 0.60.0) - React-fishhook (= 0.60.0)
- RNCAsyncStorage (1.6.1):
- React
- RNGestureHandler (1.3.0): - RNGestureHandler (1.3.0):
- React - React
- RNScreens (1.0.0-alpha.23): - RNScreens (1.0.0-alpha.23):
- React - React
- RNSecureStorage (0.1.2):
- React
- RNVectorIcons (6.6.0): - RNVectorIcons (6.6.0):
- React - React
- yoga (0.60.0.React) - yoga (0.60.0.React)
@@ -101,6 +129,7 @@ DEPENDENCIES:
- React-jsi (from `../node_modules/react-native/ReactCommon/jsi`) - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`) - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
- react-native-fbsdk (from `/Users/mfitzpatrick/Documents/Personal/eventment-app/node_modules/react-native-fbsdk`)
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`) - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
- React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`) - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`)
- React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`) - React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`)
@@ -111,14 +140,19 @@ DEPENDENCIES:
- React-RCTText (from `../node_modules/react-native/Libraries/Text`) - React-RCTText (from `../node_modules/react-native/Libraries/Text`)
- React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`) - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
- React-RCTWebSocket (from `../node_modules/react-native/Libraries/WebSocket`) - React-RCTWebSocket (from `../node_modules/react-native/Libraries/WebSocket`)
- RNGestureHandler (from `/Users/mifi/Temporary Projects/eventment-app/node_modules/react-native-gesture-handler`) - "RNCAsyncStorage (from `/Users/mfitzpatrick/Documents/Personal/eventment-app/node_modules/@react-native-community/async-storage`)"
- RNScreens (from `/Users/mifi/Temporary Projects/eventment-app/node_modules/react-native-screens`) - RNGestureHandler (from `/Users/mfitzpatrick/Documents/Personal/eventment-app/node_modules/react-native-gesture-handler`)
- RNVectorIcons (from `/Users/mifi/Temporary Projects/eventment-app/node_modules/react-native-vector-icons`) - RNScreens (from `/Users/mfitzpatrick/Documents/Personal/eventment-app/node_modules/react-native-screens`)
- RNSecureStorage (from `/Users/mfitzpatrick/Documents/Personal/eventment-app/node_modules/react-native-secure-storage`)
- RNVectorIcons (from `../node_modules/react-native-vector-icons`)
- yoga (from `../node_modules/react-native/ReactCommon/yoga`) - yoga (from `../node_modules/react-native/ReactCommon/yoga`)
SPEC REPOS: SPEC REPOS:
https://github.com/cocoapods/specs.git: https://github.com/cocoapods/specs.git:
- boost-for-react-native - boost-for-react-native
- FBSDKCoreKit
- FBSDKLoginKit
- FBSDKShareKit
EXTERNAL SOURCES: EXTERNAL SOURCES:
DoubleConversion: DoubleConversion:
@@ -143,6 +177,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/jsiexecutor" :path: "../node_modules/react-native/ReactCommon/jsiexecutor"
React-jsinspector: React-jsinspector:
:path: "../node_modules/react-native/ReactCommon/jsinspector" :path: "../node_modules/react-native/ReactCommon/jsinspector"
react-native-fbsdk:
:path: "/Users/mfitzpatrick/Documents/Personal/eventment-app/node_modules/react-native-fbsdk"
React-RCTActionSheet: React-RCTActionSheet:
:path: "../node_modules/react-native/Libraries/ActionSheetIOS" :path: "../node_modules/react-native/Libraries/ActionSheetIOS"
React-RCTAnimation: React-RCTAnimation:
@@ -163,18 +199,25 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/Libraries/Vibration" :path: "../node_modules/react-native/Libraries/Vibration"
React-RCTWebSocket: React-RCTWebSocket:
:path: "../node_modules/react-native/Libraries/WebSocket" :path: "../node_modules/react-native/Libraries/WebSocket"
RNCAsyncStorage:
:path: "/Users/mfitzpatrick/Documents/Personal/eventment-app/node_modules/@react-native-community/async-storage"
RNGestureHandler: RNGestureHandler:
:path: "/Users/mifi/Temporary Projects/eventment-app/node_modules/react-native-gesture-handler" :path: "/Users/mfitzpatrick/Documents/Personal/eventment-app/node_modules/react-native-gesture-handler"
RNScreens: RNScreens:
:path: "/Users/mifi/Temporary Projects/eventment-app/node_modules/react-native-screens" :path: "/Users/mfitzpatrick/Documents/Personal/eventment-app/node_modules/react-native-screens"
RNSecureStorage:
:path: "/Users/mfitzpatrick/Documents/Personal/eventment-app/node_modules/react-native-secure-storage"
RNVectorIcons: RNVectorIcons:
:path: "/Users/mifi/Temporary Projects/eventment-app/node_modules/react-native-vector-icons" :path: "../node_modules/react-native-vector-icons"
yoga: yoga:
:path: "../node_modules/react-native/ReactCommon/yoga" :path: "../node_modules/react-native/ReactCommon/yoga"
SPEC CHECKSUMS: SPEC CHECKSUMS:
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2 DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2
FBSDKCoreKit: 39748deefe37a005f983722e433be2ddcdd83ae7
FBSDKLoginKit: bc0329e10045789e8d40b12d4f15ddbd07a898e2
FBSDKShareKit: f61d03f36ce4f1b379c7de7dbfe173662e68be44
Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51 Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51
glog: 1f3da668190260b06b429bb211bfbee5cd790c28 glog: 1f3da668190260b06b429bb211bfbee5cd790c28
React: 4b3c068e793e96672dcd186a2b572fac43e4b031 React: 4b3c068e793e96672dcd186a2b572fac43e4b031
@@ -185,6 +228,7 @@ SPEC CHECKSUMS:
React-jsi: 8e128c4d0d8febc2977ef617d1c09bb54326069c React-jsi: 8e128c4d0d8febc2977ef617d1c09bb54326069c
React-jsiexecutor: 7a3554f703a58963ec80b860144ea0f0e9b910e1 React-jsiexecutor: 7a3554f703a58963ec80b860144ea0f0e9b910e1
React-jsinspector: d4ed52225912efe0019bb7f1a225aec20f23049a React-jsinspector: d4ed52225912efe0019bb7f1a225aec20f23049a
react-native-fbsdk: 080f3bb23513c5b59b6528543206fe0d1436bb7e
React-RCTActionSheet: b27ff3cf3a68f917c46d2b94abf938b625b96570 React-RCTActionSheet: b27ff3cf3a68f917c46d2b94abf938b625b96570
React-RCTAnimation: 9e4708e5bd65fca8285ce7c0aa076f3f4fa5c2f8 React-RCTAnimation: 9e4708e5bd65fca8285ce7c0aa076f3f4fa5c2f8
React-RCTBlob: 6eafcc3a24f33785692a7be24918ade607bc8719 React-RCTBlob: 6eafcc3a24f33785692a7be24918ade607bc8719
@@ -195,11 +239,13 @@ SPEC CHECKSUMS:
React-RCTText: 685fca2e13b024271048e7e247ef24476f28a41e React-RCTText: 685fca2e13b024271048e7e247ef24476f28a41e
React-RCTVibration: 4ee1cf208ab17a50fafb1c16ffe28fe594a64e4f React-RCTVibration: 4ee1cf208ab17a50fafb1c16ffe28fe594a64e4f
React-RCTWebSocket: fca087d583724aa0e5fef7d911f0f2a28d0f2736 React-RCTWebSocket: fca087d583724aa0e5fef7d911f0f2a28d0f2736
RNCAsyncStorage: 2e2e3feb9bdadc752a026703d8c4065ca912e75a
RNGestureHandler: 5329a942fce3d41c68b84c2c2276ce06a696d8b0 RNGestureHandler: 5329a942fce3d41c68b84c2c2276ce06a696d8b0
RNScreens: f28b48b8345f2f5f39ed6195518291515032a788 RNScreens: f28b48b8345f2f5f39ed6195518291515032a788
RNSecureStorage: 24d433f7673a0daade43689de805933a70641731
RNVectorIcons: 0bb4def82230be1333ddaeee9fcba45f0b288ed4 RNVectorIcons: 0bb4def82230be1333ddaeee9fcba45f0b288ed4
yoga: 616fde658be980aa60a2158835170f3f9c2d04b4 yoga: 616fde658be980aa60a2158835170f3f9c2d04b4
PODFILE CHECKSUM: 26223b0d86281b1c70ae009fe120443ad6ad9bf9 PODFILE CHECKSUM: 9a233bb7438f412e17eeb945a7c5f563882773de
COCOAPODS: 1.7.4 COCOAPODS: 1.6.1

7054
yarn.lock

File diff suppressed because it is too large Load Diff