Compare commits
14 Commits
master
...
feature/PR
| Author | SHA1 | Date | |
|---|---|---|---|
| e0dd85153c | |||
|
|
ee051f604e | ||
|
|
cc8442b0b2 | ||
|
|
c146884636 | ||
|
|
cf07ab1c2e | ||
|
|
f0460a1b76 | ||
| 769fc0c2b5 | |||
| 0f618fdd78 | |||
| e6a4eeaeb1 | |||
| e07a4cb2b9 | |||
| 70a8246a05 | |||
| 2643fe5d4a | |||
|
|
ebb6f47455 | ||
|
|
dfc4daf696 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -57,3 +57,4 @@ buck-out/
|
||||
|
||||
# CocoaPods
|
||||
/ios/Pods/
|
||||
*.lock
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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 { getActiveEventId } from '../selectors/activeEvent.js';
|
||||
import { getAuthToken } from '../selectors/auth.js';
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
getNomAvailaibility,
|
||||
loginUser,
|
||||
registerNewUser,
|
||||
setNomDeBid as setNomDeBidApi,
|
||||
} from '../api/profile.js';
|
||||
|
||||
import {
|
||||
@@ -15,11 +16,22 @@ import {
|
||||
PROFILE_NOM_AVAILABLE,
|
||||
REGISTRATION_FAILURE,
|
||||
REGISTRATION_SUCCESS,
|
||||
SET_NOM_FAILURE,
|
||||
SET_NOM_SUCCESS,
|
||||
UNSET_AUTH,
|
||||
UNSET_PROFILE,
|
||||
UPDATE_PROFILE,
|
||||
} 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) => ({
|
||||
type: PROFILE_EMAIL_AVAILABLE,
|
||||
payload,
|
||||
@@ -44,6 +56,16 @@ const logoutUser = () => ({
|
||||
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) => ({
|
||||
type: REGISTRATION_FAILURE,
|
||||
payload,
|
||||
@@ -71,13 +93,28 @@ export const checkEmailAvailability = (email) => (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) => {
|
||||
dispatch(blockUI());
|
||||
loginUser(username, password)
|
||||
.then((result) => {
|
||||
if (result.errors) {
|
||||
dispatch(handleLoginFailure(result.errors));
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(loginSuccess(result));
|
||||
dispatch(unblockUI());
|
||||
})
|
||||
.catch((err) => dispatch(loginFailure(err)));
|
||||
.catch((err) => dispatch(handleLoginFailure(err)));
|
||||
};
|
||||
|
||||
export const logout = () => (dispatch) => {
|
||||
|
||||
@@ -33,6 +33,10 @@ export const formatPostData = (body) => {
|
||||
return postData;
|
||||
};
|
||||
|
||||
export const formatJsonData = (body) => {
|
||||
return JSON.stringify(body);
|
||||
};
|
||||
|
||||
const parseQueryParamsString = (queryParams) => {
|
||||
if (typeof queryParams !== 'string') {
|
||||
return null;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
constructUrl,
|
||||
formatPostData,
|
||||
formatJsonData,
|
||||
parseQueryParams,
|
||||
request,
|
||||
unwrapJson,
|
||||
@@ -24,6 +25,7 @@ export const API_ENDPOINTS = {
|
||||
PURCHASE_ITEM: '/sales',
|
||||
|
||||
// User/Profile
|
||||
LOGIN: '/auth',
|
||||
USER_SIGNUP: '/signup',
|
||||
USER_PROFILE: '/users/:user_id',
|
||||
|
||||
@@ -45,11 +47,11 @@ const cacheBuster = () => {
|
||||
};
|
||||
|
||||
export const getEndpointUrl = (endpoint) => {
|
||||
if (!endpoints[endpoint]) {
|
||||
if (!API_ENDPOINTS[endpoint]) {
|
||||
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 = {}) => {
|
||||
@@ -74,9 +76,15 @@ export const requestPost = (options) => {
|
||||
queryParams = [],
|
||||
requestOptions = {},
|
||||
isFormattedPostData = false,
|
||||
shouldUseFormData = false,
|
||||
} = options;
|
||||
|
||||
const params = parseQueryParams(queryParams || []);
|
||||
let postData = isFormattedPostData ? body : null;
|
||||
|
||||
if (!postData) {
|
||||
postData = shouldUseFormData ? formatPostData(body) : formatJsonData(body);
|
||||
}
|
||||
|
||||
if (params === null) {
|
||||
throw new Error('invalid queryParams');
|
||||
@@ -88,7 +96,7 @@ export const requestPost = (options) => {
|
||||
...DefaultRequestOptions,
|
||||
...requestOptions,
|
||||
method: 'POST',
|
||||
body: isFormattedPostData ? body : formatPostData(body),
|
||||
body: postData,
|
||||
})
|
||||
.then(validateResponse)
|
||||
.then(unwrapJson);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { API_ENDPOINTS, requestGet } from './index.js';
|
||||
import { API_ENDPOINTS, requestGet, requestPost } from './index.js';
|
||||
|
||||
export const getEmailAvailability = (email) =>
|
||||
requestGet(`${API_ENDPOINTS.VALIDATE_SIGNUP_EMAIL}/&{encodeURI(email)}`);
|
||||
requestGet(`${API_ENDPOINTS.VALIDATE_SIGNUP_EMAIL}/${encodeURI(email)}`);
|
||||
|
||||
export const getNomAvailaibility = (nomDeBid) =>
|
||||
requestGet(`${API_ENDPOINTS.VALIDATE_SIGNUP_NOM}/${encodeURI(nomDeBid)}`);
|
||||
@@ -17,3 +17,10 @@ export const registerNewUser = (user) =>
|
||||
path: API_ENDPOINTS.USER_SIGNUP,
|
||||
body: { user },
|
||||
});
|
||||
|
||||
export const setNomDeBid = (id, auth) => (nomDeBid) =>
|
||||
requestPost({
|
||||
path: `${API_ENDPOINTS.SET_NOM}/${id}`,
|
||||
body: { nomDeBid },
|
||||
requestOptions: { Authorization: auth ? `Bearer ${auth}` : null },
|
||||
});
|
||||
|
||||
@@ -7,8 +7,6 @@ import HeaderTitle from './HeaderTitle/HeaderTitle.container.js';
|
||||
import HeaderContentLeft from './HeaderContentLeft.container.js';
|
||||
import HeaderContentRight from './HeaderContentRight.container.js';
|
||||
|
||||
import styles from './AppHeader.styles.js';
|
||||
|
||||
export default class AppHeader extends Component {
|
||||
static get propTypes() {
|
||||
return {
|
||||
@@ -26,9 +24,9 @@ export default class AppHeader extends Component {
|
||||
return (
|
||||
<Header
|
||||
placement="left"
|
||||
leftComponent={<HeaderContentRight navigation={navigation} />}
|
||||
centerComponent={<HeaderTitle navigation={navigation} />}
|
||||
rightComponent={<HeaderContentLeft navigation={navigation} />}
|
||||
leftComponent={<HeaderContentLeft navigation={navigation} />}
|
||||
rightComponent={<HeaderContentRight navigation={navigation} />}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { Text, TouchableOpacity, View } from 'react-native';
|
||||
|
||||
import BackIcon from './IconButtons/BackIcon.js';
|
||||
import EventsIcon from './IconButtons/EventsIcon.js';
|
||||
|
||||
export default function HeaderContentLeft({ activeRoute, hasMultipleEvents, navigation }) {
|
||||
export default function HeaderContentLeft({
|
||||
activeRoute,
|
||||
hasActiveEvent,
|
||||
hasMultipleEvents,
|
||||
navigation,
|
||||
}) {
|
||||
const _goBack = () => {
|
||||
if (hasActiveEvent) {
|
||||
navigation.goBack();
|
||||
@@ -29,15 +32,20 @@ export default function HeaderContentLeft({ activeRoute, hasMultipleEvents, navi
|
||||
return <BackIcon action={_goBack} />;
|
||||
}
|
||||
|
||||
return <EventsIcon action={hasMultipleEvents ? _showEvents : null} />;
|
||||
if (hasMultipleEvents) {
|
||||
return <EventsIcon action={_showEvents} />;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
HeaderContentLeft.propTypes = {
|
||||
activeRoute: PropTypes.string.isRequired,
|
||||
hasActiveEvent: PropTypes.bool,
|
||||
navigation: PropTypes.func.isRequired,
|
||||
hasMultipleEvents: PropTypes.bool,
|
||||
};
|
||||
|
||||
HeaderContentLeft.defaultProps = {
|
||||
hasActiveEvent: false,
|
||||
hasMultipleEvents: false,
|
||||
};
|
||||
|
||||
@@ -6,7 +6,9 @@ import HeaderContentRight from './HeaderContentRight.js';
|
||||
|
||||
const matchStateToProps = (state, ownProps) => ({
|
||||
avatarUrl: getProfileAvatarUrl(state),
|
||||
hideUserProfileButton: ownProps.navigation.state.routeName === 'Profile',
|
||||
hideUserProfileButton:
|
||||
['Profile', 'Register', 'SignInOrRegister'].indexOf(ownProps.navigation.state.routeName) >
|
||||
-1,
|
||||
});
|
||||
|
||||
export default connect(
|
||||
|
||||
@@ -8,7 +8,7 @@ export default function HeaderContentRight({ hideUserProfileButton, navigation }
|
||||
return null;
|
||||
}
|
||||
|
||||
return <UserProfileButton />;
|
||||
return <UserProfileButton navigation={navigation} />;
|
||||
}
|
||||
|
||||
HeaderContentRight.propTypes = {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { hasActiveEvent } from '../../../../selectors/activeEvent.js';
|
||||
import { getActiveEvent, getDefaultEvent } from '../../../../selectors/events.js';
|
||||
|
||||
import EventTitle from './EventTitle.js';
|
||||
|
||||
@@ -6,12 +6,16 @@ import { Text, TouchableOpacity, View } from 'react-native';
|
||||
import styles from './EventTitle.styles.js';
|
||||
|
||||
export default function EventTitle({ action, date, end, name, start }) {
|
||||
const _generateEventTitle = () => (
|
||||
<View style={styles.eventInfo}>
|
||||
<Text style={styles.eventName}>{name}</Text>
|
||||
<Text style={styles.eventDate}>{`${date} | ${start} - ${end}`}</Text>
|
||||
</View>
|
||||
);
|
||||
const _generateEventTitle = () => {
|
||||
const whenString = `${date} | ${start} - ${end}`;
|
||||
|
||||
return (
|
||||
<View style={styles.eventInfo}>
|
||||
<Text style={styles.eventName}>{name}</Text>
|
||||
<Text style={styles.eventDate}>{whenString}</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
if (action) {
|
||||
return <TouchableOpacity onPress={action}>{_generateEventTitle()}</TouchableOpacity>;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
export default (styles = StyleSheet.create({
|
||||
export default StyleSheet.create({
|
||||
eventInfo: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
@@ -11,4 +11,4 @@ export default (styles = StyleSheet.create({
|
||||
eventDate: {
|
||||
flex: 1,
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import React from 'react';
|
||||
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 styles from './HeaderTitle.styles.js';
|
||||
|
||||
const STRINGS = {
|
||||
EVENTS: 'Events',
|
||||
PROFILE: 'Profile',
|
||||
};
|
||||
|
||||
export default function HeaderTitle({
|
||||
activeRoute,
|
||||
hasActiveEvent,
|
||||
@@ -30,13 +35,13 @@ export default function HeaderTitle({
|
||||
if (activeRoute === 'Events') {
|
||||
return (
|
||||
<TouchableOpacity onPress={_goBack}>
|
||||
<Text style={styles.screenHeader}>Profile</Text>
|
||||
<Text style={styles.screenHeader}>{STRINGS.EVENTS}</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
if (activeRoute === 'Profile') {
|
||||
return <Text style={styles.screenHeader}>Profile</Text>;
|
||||
return <Text style={styles.screenHeader}>{STRINGS.PROFILE}</Text>;
|
||||
}
|
||||
|
||||
return <EventTitle action={hasMultipleEvents ? _showEvents : null} />;
|
||||
@@ -46,7 +51,6 @@ HeaderTitle.propTypes = {
|
||||
activeRoute: PropTypes.string.isRequired,
|
||||
hasActiveEvent: PropTypes.bool,
|
||||
hasMultipleEvents: PropTypes.bool.isRequired,
|
||||
navigation: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
HeaderTitle.defaultProps = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
export default (styles = StyleSheet.create({
|
||||
export default StyleSheet.create({
|
||||
filterBar: {
|
||||
backgroundColor: '#0F0',
|
||||
flexDirection: 'row',
|
||||
@@ -11,4 +11,4 @@ export default (styles = StyleSheet.create({
|
||||
view: {
|
||||
flex: 2,
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -2,12 +2,12 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { TouchableOpacity } from 'react-native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import Icon from 'react-native-vector-icons';
|
||||
|
||||
export default function BackIcon({ action }) {
|
||||
return (
|
||||
<TouchableOpacity onPress={action}>
|
||||
<Icon name="ei-chevron-left" type="evilicons" size={28} />;
|
||||
<Icon name="ei-chevron-left" type="evilicons" size={28} />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { TouchableOpacity } from 'react-native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
|
||||
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) {
|
||||
return <TouchableOpacity onPress={action}>{renderEventsIcon()}</TouchableOpacity>;
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { getProfileAvatarUrl } from '../../../selectors/profile.js';
|
||||
import {
|
||||
getProfileAvatarUrl,
|
||||
getUserInitials,
|
||||
isRegisteredAccount,
|
||||
} from '../../../selectors/profile.js';
|
||||
|
||||
import UserProfileButton from './UserProfileButton.js';
|
||||
|
||||
const matchStateToProps = (state) => ({
|
||||
avatarUrl: getProfileAvatarUrl(state),
|
||||
initials: getUserInitials(state),
|
||||
isRegisteredAccount: isRegisteredAccount(state),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
|
||||
@@ -2,22 +2,33 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
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';
|
||||
|
||||
export default function UserProfileButton({ avatarUrl, navigation }) {
|
||||
export default function UserProfileButton({
|
||||
avatarUrl,
|
||||
initials,
|
||||
isRegisteredAccount,
|
||||
navigation,
|
||||
}) {
|
||||
const _goToProfile = () => {
|
||||
navigation.navigate('Profile');
|
||||
return false;
|
||||
if (isRegisteredAccount) {
|
||||
navigation.navigate('Profile');
|
||||
return false;
|
||||
}
|
||||
|
||||
navigation.navigate('SignInOrRegister');
|
||||
};
|
||||
|
||||
return (
|
||||
<TouchableOpacity onPress={_goToProfile}>
|
||||
{avatarUrl !== null ? (
|
||||
<View style={styles.avatarWrap}>
|
||||
<Image source={{ uri: avatarUrl }} />
|
||||
</View>
|
||||
{isRegisteredAccount !== null ? (
|
||||
avatarUrl !== null ? (
|
||||
<Avatar source={{ uri: avatarUrl }} />
|
||||
) : (
|
||||
<Avatar title={initials} />
|
||||
)
|
||||
) : (
|
||||
<Icon name="ei-user" type="evilicons" size={28} />
|
||||
)}
|
||||
@@ -27,8 +38,12 @@ export default function UserProfileButton({ avatarUrl, navigation }) {
|
||||
|
||||
UserProfileButton.propTypes = {
|
||||
avatarUrl: PropTypes.string,
|
||||
initials: PropTypes.string,
|
||||
isRegisteredAccount: PropTypes.bool,
|
||||
};
|
||||
|
||||
UserProfileButton.propTypes = {
|
||||
UserProfileButton.defaultProps = {
|
||||
avatarUrl: null,
|
||||
initials: null,
|
||||
isRegisteredAccount: false,
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import { connect } from 'react-redux';
|
||||
|
||||
import { placeBid } from '../../actions/auction.js';
|
||||
|
||||
import AuctionListItem from '../../components/Auction/AuctionListItem.js';
|
||||
import AuctionListItem from './AuctionListItem.js';
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const { item } = ownProps;
|
||||
@@ -27,5 +27,5 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
null,
|
||||
mapDispatchToProps,
|
||||
)(AuctionListItem);
|
||||
@@ -5,8 +5,8 @@ import { StyleSheet, TouchableOpacity, Text, Image, View } from 'react-native';
|
||||
|
||||
import GallerySwiper from 'react-native-gallery-swiper';
|
||||
|
||||
import AuctionPriceAndBidCount from '../../containers/Auction/AuctionPriceAndBidCount.js';
|
||||
import BidStatus from '../../containers/Auction/BidStatus.js';
|
||||
import AuctionPriceAndBidCount from './AuctionPriceAndBidCount.container.js';
|
||||
import BidStatus from './BidStatus.container.js';
|
||||
|
||||
import { ITEM_TYPES } from '../../constants/constants.js';
|
||||
import { formatPrice, getAuctionTime } from '../../library/helpers.js';
|
||||
@@ -83,28 +83,28 @@ export default class AuctionListItem extends Component {
|
||||
)}
|
||||
<View style={styles.rowText}>
|
||||
{type === ITEM_TYPES.AUCTION && <BidStatus itemId={id} />}
|
||||
<Text style={styles.title} numberOfLines={2} ellipsizeMode={'tail'}>
|
||||
<Text style={styles.title} numberOfLines={2} ellipsizeMode="tail">
|
||||
{title}
|
||||
</Text>
|
||||
<Text style={styles.subtitle} numberOfLines={1} ellipsizeMode={'tail'}>
|
||||
<Text style={styles.subtitle} numberOfLines={1} ellipsizeMode="tail">
|
||||
{subtitle}
|
||||
</Text>
|
||||
{donor && (
|
||||
<Text style={styles.donor} numberOfLines={1} ellipsizeMode={'tail'}>
|
||||
<Text style={styles.donor} numberOfLines={1} ellipsizeMode="tail">
|
||||
{donor}
|
||||
</Text>
|
||||
)}
|
||||
{type === ITEM_TYPES.AUCTION ? (
|
||||
<AuctionPriceAndBidCount itemId={id} />
|
||||
) : (
|
||||
<Text style={styles.price} numberOfLines={1} ellipsizeMode={'tail'}>
|
||||
<Text style={styles.price} numberOfLines={1} ellipsizeMode="tail">
|
||||
{formatPrice(startingPrice)}
|
||||
</Text>
|
||||
)}
|
||||
<Text style={styles.timeline} numberOfLines={1}>
|
||||
{this._getBidTime()}
|
||||
</Text>
|
||||
<Text style={styles.description} numberOfLines={3} ellipsizeMode={'tail'}>
|
||||
<Text style={styles.description} numberOfLines={3} ellipsizeMode="tail">
|
||||
{description}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { connect } from 'react-redux';
|
||||
|
||||
import { getItemBidCount, getItemPrice } from '../../selectors/auctions.js';
|
||||
|
||||
import AuctionPriceAndBidCount from '../../components/Auction/AuctionPriceAndBidCount.js';
|
||||
import AuctionPriceAndBidCount from './AuctionPriceAndBidCount.js';
|
||||
|
||||
function mapStateToProps(state, ownProps) {
|
||||
const { itemId } = ownProps;
|
||||
@@ -6,9 +6,11 @@ import { formatPrice } from '../../library/helpers.js';
|
||||
import { StyleSheet, Text } from 'react-native';
|
||||
|
||||
const AuctionPriceAndBidCount = ({ bidCount, currentPrice }) => {
|
||||
const _getPriceAndBidString = () => `${formatPrice(currentPrice)} (${bidCount} bids)`;
|
||||
|
||||
return (
|
||||
<Text style={styles.currentPriceAndBidCount} numberOfLines={1}>
|
||||
{`${formatPrice(currentPrice)} (${bidCount} bids)`}
|
||||
{_getPriceAndBidString()}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import { connect } from 'react-redux';
|
||||
|
||||
import { isBiddingItem, isWinningItem } from '../../selectors/auctions.js';
|
||||
|
||||
import AuctionPriceAndBidCount from '../../components/Auction/BidStatus.js';
|
||||
import AuctionPriceAndBidCount from './BidStatus.js';
|
||||
|
||||
function mapStateToProps(state, ownProps) {
|
||||
const { itemId } = ownProps;
|
||||
@@ -9,7 +9,7 @@ const BidStatus = ({ isBidding, isWinning }) => {
|
||||
}
|
||||
|
||||
const statusBarStyle = isWinning
|
||||
? [styles.bidStatus, styes.isWinning]
|
||||
? [styles.bidStatus, styles.isWinning]
|
||||
: [styles.bidStatus, styles.isOutbid];
|
||||
|
||||
return (
|
||||
|
||||
@@ -3,10 +3,15 @@ import PropTypes from 'prop-types';
|
||||
|
||||
import { StyleSheet, Text, View } from 'react-native';
|
||||
|
||||
const STRINGS = {
|
||||
FILTER: 'Filter',
|
||||
VIEW: 'View',
|
||||
};
|
||||
|
||||
const FilterBar = ({ changeFilterer, changeViewMode, filterMode, viewMode }) => (
|
||||
<View style={styles.filterBar}>
|
||||
<Text style={styles.filter}>Filter</Text>
|
||||
<Text style={styles.view}>View</Text>
|
||||
<Text style={styles.filter}>{STRINGS.FILTER}</Text>
|
||||
<Text style={styles.view}>{STRINGS.VIEW}</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { setActiveEvent } from '../../actions/events.js';
|
||||
import EventListItem from '../../components/Events/EventListItem.js';
|
||||
import EventListItem from './EventListItem.js';
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const { event } = ownProps;
|
||||
@@ -27,5 +27,5 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
null,
|
||||
)(AuctionListItem);
|
||||
mapDispatchToProps,
|
||||
)(EventListItem);
|
||||
@@ -33,24 +33,28 @@ export default class EventListItem extends Component {
|
||||
|
||||
constructor(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.navigation.navigate('Event');
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const { date, description, end, name, start } = this.props;
|
||||
const { date, description, name } = this.props;
|
||||
|
||||
return (
|
||||
<TouchableOpacity onPress={this._viewEventDetail}>
|
||||
<View style={styles.rowContainer}>
|
||||
<Text>{name}</Text>
|
||||
<Text>{date}</Text>
|
||||
<Text>
|
||||
{start} - {end}
|
||||
</Text>
|
||||
<Text>{this.getTimeString()}</Text>
|
||||
<Text>{description}</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
|
||||
@@ -3,54 +3,51 @@ import PropTypes from 'prop-types';
|
||||
|
||||
import { Button, TextInput, View } from 'react-native';
|
||||
|
||||
import styles from './Login.styles.js';
|
||||
|
||||
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 [ password, setPassword ] = useState(null);
|
||||
const [ username, setUsername ] = useState(null);
|
||||
const _handleLoginSubmit = () => {
|
||||
doLoginAction(username, password);
|
||||
};
|
||||
|
||||
const _handleLoginSubmit = () => {
|
||||
doLoginAction(username, password);
|
||||
};
|
||||
const _updateState = (field, value) => {
|
||||
if (field === 'username') {
|
||||
setUsername(value);
|
||||
}
|
||||
|
||||
const _updateState = (field, value) => {
|
||||
if (field === 'username') {
|
||||
setUsername(value);
|
||||
}
|
||||
if (field === 'password') {
|
||||
setPassword(value);
|
||||
}
|
||||
|
||||
if (field === 'password') {
|
||||
setPassword(value);
|
||||
}
|
||||
if (!!username && !!password) {
|
||||
setEnableSubmit(true);
|
||||
}
|
||||
};
|
||||
|
||||
if (!!username && !!password) {
|
||||
setEnableSubmit(true);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.loginWrap}>
|
||||
<TextInput
|
||||
keyboardType="email-address"
|
||||
onChangeText={(text) => _updateState('username', text)}
|
||||
placeholder="email"
|
||||
style={{height: 40}}
|
||||
value={username}
|
||||
/>
|
||||
<TextInput
|
||||
enablesReturnKeyAutomatically
|
||||
onChangeText={(text) => _updateState('password', text)}
|
||||
placeholder="password"
|
||||
secureTextEntry
|
||||
style={{height: 40}}
|
||||
value={password}
|
||||
/>
|
||||
<Button
|
||||
disabled={!enabled}
|
||||
onPress={_handleLoginSubmit}
|
||||
title="Login"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
return (
|
||||
<View style={styles.loginWrap}>
|
||||
<TextInput
|
||||
keyboardType="email-address"
|
||||
onChangeText={(text) => _updateState('username', text)}
|
||||
placeholder="email"
|
||||
style={styles.textInput}
|
||||
value={username}
|
||||
/>
|
||||
<TextInput
|
||||
enablesReturnKeyAutomatically
|
||||
onChangeText={(text) => _updateState('password', text)}
|
||||
placeholder="password"
|
||||
secureTextEntry
|
||||
style={styles.textInput}
|
||||
value={password}
|
||||
/>
|
||||
<Button disabled={!enabled} onPress={_handleLoginSubmit} title="Login" />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
LocalLogin.propTypes = {
|
||||
|
||||
8
app/components/Login/Login.styles.js
Normal file
8
app/components/Login/Login.styles.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
export default StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
textInput: {},
|
||||
});
|
||||
225
app/components/Profile/CreateProfile.js
Normal file
225
app/components/Profile/CreateProfile.js
Normal 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>
|
||||
);
|
||||
}
|
||||
}
|
||||
21
app/components/Profile/EditProfile.container.js
Normal file
21
app/components/Profile/EditProfile.container.js
Normal 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);
|
||||
234
app/components/Profile/EditProfile.js
Normal file
234
app/components/Profile/EditProfile.js
Normal 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>
|
||||
);
|
||||
}
|
||||
}
|
||||
9
app/components/Profile/Profile.container.js
Normal file
9
app/components/Profile/Profile.container.js
Normal 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);
|
||||
59
app/components/Profile/Profile.js
Normal file
59
app/components/Profile/Profile.js
Normal 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,
|
||||
};
|
||||
16
app/components/Profile/Profile.stateProps.js
Normal file
16
app/components/Profile/Profile.stateProps.js
Normal 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'),
|
||||
};
|
||||
};
|
||||
36
app/components/Profile/Profile.styles.js
Normal file
36
app/components/Profile/Profile.styles.js
Normal 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',
|
||||
},
|
||||
});
|
||||
@@ -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);
|
||||
79
app/components/Profile/ProfileInputs/EditNomDeBid.js
Normal file
79
app/components/Profile/ProfileInputs/EditNomDeBid.js
Normal 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,
|
||||
};
|
||||
86
app/components/Profile/ProfileInputs/EmailInput.js
Normal file
86
app/components/Profile/ProfileInputs/EmailInput.js
Normal 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,
|
||||
}
|
||||
53
app/components/Profile/ProfileInputs/PasswordInput.js
Normal file
53
app/components/Profile/ProfileInputs/PasswordInput.js
Normal 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,
|
||||
};
|
||||
122
app/components/Profile/ProfileInputs/PhoneListInput.js
Normal file
122
app/components/Profile/ProfileInputs/PhoneListInput.js
Normal 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>
|
||||
);
|
||||
}
|
||||
}
|
||||
76
app/components/Profile/ProfileInputs/PhoneListItem.js
Normal file
76
app/components/Profile/ProfileInputs/PhoneListItem.js
Normal 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,
|
||||
};
|
||||
|
||||
|
||||
|
||||
21
app/components/Profile/ViewProfile.container.js
Normal file
21
app/components/Profile/ViewProfile.container.js
Normal 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);
|
||||
104
app/components/Profile/ViewProfile.js
Normal file
104
app/components/Profile/ViewProfile.js
Normal 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(),
|
||||
};
|
||||
@@ -50,6 +50,9 @@ export const UNSET_PROFILE = 'UNSET_PROFILE';
|
||||
export const UPDATE_PROFILE = 'UPDATE_PROFILE';
|
||||
|
||||
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 ADD_PAYMENT_DATA = 'ADD_PAYMENT_DATA';
|
||||
|
||||
@@ -51,3 +51,11 @@ export const API_ENDPOINTS = {
|
||||
export const PERMISSIONS = {
|
||||
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;
|
||||
|
||||
@@ -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);
|
||||
@@ -28,6 +28,17 @@ export default class Profile extends Record({
|
||||
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() {
|
||||
return (
|
||||
this.hasLinkedApple ||
|
||||
|
||||
@@ -13,9 +13,20 @@ import ImageDetail from './screens/ImageDetail.js';
|
||||
import Item from './screens/Item.js';
|
||||
import Marketplace from './screens/Marketplace.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';
|
||||
|
||||
const tabBarVisibility = ({ navigation }) => {
|
||||
let tabBarVisible = true;
|
||||
if (navigation.state.index > 0) {
|
||||
tabBarVisible = false;
|
||||
}
|
||||
|
||||
return {
|
||||
tabBarVisible,
|
||||
};
|
||||
};
|
||||
|
||||
export const SignInOrRegisterStack = createStackNavigator({
|
||||
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({
|
||||
Auction: {
|
||||
screen: Auction,
|
||||
@@ -60,6 +98,8 @@ export const AuctionStack = createStackNavigator({
|
||||
},
|
||||
});
|
||||
|
||||
AuctionStack.navigationOptions = tabBarVisibility;
|
||||
|
||||
export const BazaarStack = createStackNavigator({
|
||||
Bazaar: {
|
||||
screen: Marketplace,
|
||||
@@ -93,12 +133,13 @@ export const BazaarStack = createStackNavigator({
|
||||
},
|
||||
});
|
||||
|
||||
BazaarStack.navigationOptions = tabBarVisibility;
|
||||
|
||||
export const EventsStack = createStackNavigator({
|
||||
Events: {
|
||||
screen: Events,
|
||||
navigationOptions: ({ navigation }) => ({
|
||||
header: <AppHeader navigation={navigation} />,
|
||||
tabBarVisible: false,
|
||||
gesturesEnabled: false,
|
||||
}),
|
||||
},
|
||||
@@ -106,12 +147,13 @@ export const EventsStack = createStackNavigator({
|
||||
screen: Event,
|
||||
navigationOptions: ({ navigation }) => ({
|
||||
header: <AppHeader navigation={navigation} />,
|
||||
tabBarVisible: false,
|
||||
gesturesEnabled: false,
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
EventsStack.navigationOptions = tabBarVisibility;
|
||||
|
||||
export const Tabs = createBottomTabNavigator({
|
||||
Event: {
|
||||
screen: EventsStack,
|
||||
@@ -141,52 +183,12 @@ export const Tabs = createBottomTabNavigator({
|
||||
},
|
||||
},
|
||||
Profile: {
|
||||
screen: Profile,
|
||||
screen: ProfileStack,
|
||||
navigationOptions: {
|
||||
tabBarLabel: 'Profile',
|
||||
tabBarIcon: ({ tintColor }) => (
|
||||
<Icon name="ios-person" type="font-awesome" size={28} color={tintColor} />
|
||||
<Icon name="user" type="evilicon" size={28} color={tintColor} />
|
||||
),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
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',
|
||||
},
|
||||
);
|
||||
};
|
||||
}, { initialRouteName: 'Event' });
|
||||
|
||||
@@ -7,17 +7,16 @@ import { getAuctionItemsAsList } from '../selectors/items.js';
|
||||
|
||||
import Auction from './Auction.js';
|
||||
|
||||
const matchStateToProps = (state) => {
|
||||
const items = getAuctionItemsAsList(state);
|
||||
console.log('items:', items);
|
||||
const changeViewMode = () => true;
|
||||
|
||||
return { items };
|
||||
};
|
||||
const matchStateToProps = (state) => ({
|
||||
items: getAuctionItemsAsList(state),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
changeViewMode: (mode) => dispatch(changeViewMode(mode)),
|
||||
fetchItems: () => dispatch(fetchItems(dispatch)),
|
||||
fetchStatus: () => dispatch(fetchAuctionStatus(dispatch)),
|
||||
fetchItems: () => dispatch(fetchItems()),
|
||||
fetchStatus: () => dispatch(fetchAuctionStatus()),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
|
||||
@@ -7,7 +7,7 @@ import { FlatList, Text, View } from 'react-native';
|
||||
import { SORT_MODES, AUCTION_VIEW_MODES } from '../constants/constants.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';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
export default (styles = StyleSheet.create({
|
||||
export default StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
@@ -14,4 +14,4 @@ export default (styles = StyleSheet.create({
|
||||
alignItems: 'stretch',
|
||||
justifyContent: 'flex-start',
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -3,9 +3,11 @@ import { StyleSheet, Text, View } from 'react-native';
|
||||
|
||||
export default class Checkout extends Component {
|
||||
render() {
|
||||
const title = 'Checkout';
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>Checkout</Text>
|
||||
<Text style={styles.title}>{title}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ export default class Event extends Component {
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>Event</Text>
|
||||
<Text style={styles.title}>{title}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
export default (styles = StyleSheet.create({
|
||||
export default StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
@@ -12,4 +12,4 @@ export default (styles = StyleSheet.create({
|
||||
textAlign: 'center',
|
||||
margin: 10,
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -6,10 +6,9 @@ import { getEventsAsList } from '../selectors/events.js';
|
||||
|
||||
import Events from './Events.js';
|
||||
|
||||
const matchStateToProps = (state) => {
|
||||
const events = getEventsAsList(state);
|
||||
return { events };
|
||||
};
|
||||
const matchStateToProps = (state) => ({
|
||||
events: getEventsAsList(state),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
fetchEvents: () => dispatch(fetchEvents()),
|
||||
|
||||
@@ -2,14 +2,14 @@ import { List } from 'immutable';
|
||||
import React, { Component } from 'react';
|
||||
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 {
|
||||
static get propTypes() {
|
||||
return {
|
||||
events: PropTypes.instanceOf(List),
|
||||
events: PropTypes.oneOfType([PropTypes.array, PropTypes.instanceOf(List)]),
|
||||
fetchEvents: PropTypes.func.isRequired,
|
||||
setActiveEvent: PropTypes.func.isRequired,
|
||||
};
|
||||
@@ -17,36 +17,32 @@ export default class Events extends Component {
|
||||
|
||||
static get defaultProps() {
|
||||
return {
|
||||
events: new List(),
|
||||
events: [],
|
||||
};
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this._renderEventListItem = this._renderEventListItem.bind(this);
|
||||
this._setActiveEvent = this.setActiveEvent.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.fetchEvents();
|
||||
}
|
||||
|
||||
setActiveEvent(eventId) {
|
||||
this.props.setActiveEvent(eventId);
|
||||
}
|
||||
|
||||
_keyExtractor = (event, index) => `${event.id}_${index}`;
|
||||
|
||||
_renderEventListItem = ({ event }) => (
|
||||
<EventListItem {...event} setActiveEvent={this.setActiveEvent} />
|
||||
);
|
||||
_renderEventListItem({ event }) {
|
||||
return <EventListItem event={event} setActiveEvent={this.setActiveEvent} />;
|
||||
};
|
||||
|
||||
render() {
|
||||
const { events } = this.props;
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{events.size > 0 && (
|
||||
{events.size > 0 ? (
|
||||
<FlatList
|
||||
data={events}
|
||||
keyExtractor={this._keyExtractor}
|
||||
@@ -54,6 +50,8 @@ export default class Events extends Component {
|
||||
contentContainerStyle={styles.eventListContentContainer}
|
||||
style={styles.eventList}
|
||||
/>
|
||||
) : (
|
||||
<ActivityIndicator animating={true} size="large" />
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
@@ -67,5 +65,8 @@ const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#F5FCFF',
|
||||
},
|
||||
eventListContentContainer: {},
|
||||
eventListContentContainer: {
|
||||
alignItems: 'stretch',
|
||||
justifyContent: 'flex-start',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -3,9 +3,11 @@ import { StyleSheet, Text, View } from 'react-native';
|
||||
|
||||
export default class ImageDetail extends Component {
|
||||
render() {
|
||||
const title = 'Item';
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>Item</Text>
|
||||
<Text style={styles.title}>{title}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,9 +3,11 @@ import { StyleSheet, Text, View } from 'react-native';
|
||||
|
||||
export default class Item extends Component {
|
||||
render() {
|
||||
const title = 'Item';
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>Item</Text>
|
||||
<Text style={styles.title}>{title}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
});
|
||||
@@ -7,9 +7,9 @@ import { FlatList, Text, View } from 'react-native';
|
||||
import { SORT_MODES, AUCTION_VIEW_MODES } from '../constants/constants.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 {
|
||||
static get propTypes() {
|
||||
|
||||
35
app/screens/Modal.js
Normal file
35
app/screens/Modal.js
Normal 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;
|
||||
@@ -1,7 +1,7 @@
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
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';
|
||||
|
||||
@@ -13,13 +13,8 @@ const matchStateToProps = (state) => {
|
||||
hasLinkedFacebook: profile.get('hasLinkedFacebook'),
|
||||
hasLinkedGoogle: profile.get('hasLinkedGoogle'),
|
||||
hasLocalAccount: profile.get('hasLocalAccount'),
|
||||
hasRegisteredAcccount: profile.get('hasRegisteredAcccount'),
|
||||
id: profile.get('id'),
|
||||
isAllowedToBid: isAllowedToBid(state),
|
||||
isVerified: profile.get('isVerified'),
|
||||
lastName: profile.get('lastName'),
|
||||
nomDeBid: getNomDeBid(state),
|
||||
organizationIdentifier: profile.get('organizationIdentifier'),
|
||||
paymentToken: profile.get('paymentToken'),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,26 +1,95 @@
|
||||
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 {
|
||||
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() {
|
||||
const {
|
||||
hasLinkedApple,
|
||||
hasLinkedFacebook,
|
||||
hasLinkedGoogle,
|
||||
hasLocalAccount,
|
||||
isAllowedToBid,
|
||||
isVerified,
|
||||
paymentToken,
|
||||
updateProfile,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#F5FCFF',
|
||||
},
|
||||
title: {
|
||||
fontSize: 20,
|
||||
textAlign: 'center',
|
||||
margin: 10,
|
||||
},
|
||||
});
|
||||
|
||||
15
app/screens/Profile.styles.js
Normal file
15
app/screens/Profile.styles.js
Normal 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,
|
||||
},
|
||||
});
|
||||
@@ -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 CreateProfile from '../components/Profile/CreateProfile.js';
|
||||
|
||||
import styles from './Register.styles.js';
|
||||
|
||||
const STRINGS = {
|
||||
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.',
|
||||
SUBMIT_REGISTRATION: 'Register',
|
||||
};
|
||||
export default function Register({ doRegistration, navigation }) {
|
||||
const title = 'Register';
|
||||
|
||||
export default class Register extends Component {
|
||||
static get propTypes() {
|
||||
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.');
|
||||
const _doRegistration = (profile) => {
|
||||
if (!profile) {
|
||||
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 (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>Register</Text>
|
||||
<View style={styles.nameWrap}>
|
||||
<TextInput
|
||||
onChange={(text) => this.setState({ firstName: text })}
|
||||
placeholder="first name"
|
||||
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 style={styles.container}>
|
||||
<Text style={styles.heading}>{title}</Text>
|
||||
<CreateProfile
|
||||
cancelEditAction={() => navigation.goBack()}
|
||||
saveProfileAction={_doRegistration}
|
||||
showPasswordEntry
|
||||
/>
|
||||
</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,
|
||||
};
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
export default (styles = StyleSheet.create({
|
||||
export default StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
},
|
||||
title: {},
|
||||
localLogin: {},
|
||||
services: {},
|
||||
register: {},
|
||||
}));
|
||||
heading: {},
|
||||
});
|
||||
|
||||
@@ -1,37 +1,26 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { Button } from 'react-native-elements';
|
||||
import { Button, Text, View } from 'react-native';
|
||||
|
||||
import FacebookLogin from '../components/Login/FacebookLogin.container.js';
|
||||
import LocalLogin from '../components/Login/LocalLogin.container.js';
|
||||
|
||||
import styles from './SignInOrRegister.styles.js';
|
||||
|
||||
export default class SignInOrRegister extends Component {
|
||||
constructor() {
|
||||
super(props);
|
||||
export default function SignInOrRegister({ navigation }) {
|
||||
const title = 'Sign In or Register';
|
||||
|
||||
this._doRegistration = this._doRegistration.bind(this);
|
||||
}
|
||||
|
||||
_doRegistration() {
|
||||
this.props.navigation.navigate('Register');
|
||||
}
|
||||
|
||||
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>
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>{title}</Text>
|
||||
<View style={styles.localLogin}>
|
||||
<LocalLogin />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
<View style={styles.services}>
|
||||
<FacebookLogin />
|
||||
</View>
|
||||
<View style={styles.register}>
|
||||
<Button title="Signup with Email" onPress={() => navigation.navigate('Register')} />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
export default (styles = StyleSheet.create({
|
||||
export default StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
@@ -12,4 +12,4 @@ export default (styles = StyleSheet.create({
|
||||
textAlign: 'center',
|
||||
margin: 10,
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -3,9 +3,11 @@ import { StyleSheet, Text, View } from 'react-native';
|
||||
|
||||
export default class Ticketing extends Component {
|
||||
render() {
|
||||
const title = 'Ticketing';
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>Ticketing</Text>
|
||||
<Text style={styles.title}>{title}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Map } from 'immutable';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
const getState = (state) => state;
|
||||
@@ -16,7 +17,7 @@ export const getAuctionStatus = (state, itemId) => state.getIn(['actions', itemI
|
||||
|
||||
export const getAuctionStatuses = createSelector(
|
||||
[getState],
|
||||
(state) => state.get('actions') || new Map(),
|
||||
(state) => state.get('autions') || new Map(),
|
||||
);
|
||||
|
||||
export const getItemsIdsWithNoBids = createSelector(
|
||||
|
||||
@@ -18,8 +18,8 @@ export const getActiveEvent = createSelector(
|
||||
);
|
||||
|
||||
export const getDefaultEvent = createSelector(
|
||||
[getEvents],
|
||||
(eventsAsMap) => eventsAsMap.first(),
|
||||
[getActiveEvent, getEvents],
|
||||
(activeEvent, eventsAsMap) => activeEvent || eventsAsMap.first(),
|
||||
);
|
||||
|
||||
export const getEventsAsList = createSelector(
|
||||
|
||||
@@ -18,7 +18,27 @@ export const getProfileAvatarUrl = createSelector(
|
||||
(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(
|
||||
[getProfile],
|
||||
(profile) => profile.get('isAllowedToBid'),
|
||||
);
|
||||
|
||||
export const isGeneratedNomDeBid = createSelector(
|
||||
[getProfile],
|
||||
(profile) => profile.get('generatedNomDeBid'),
|
||||
);
|
||||
|
||||
export const isRegisteredAccount = createSelector(
|
||||
[getProfile],
|
||||
(profile) => profile.get('isRegisteredAccount'),
|
||||
);
|
||||
|
||||
@@ -848,9 +848,43 @@
|
||||
);
|
||||
inputPaths = (
|
||||
"${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";
|
||||
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;
|
||||
shellPath = /bin/sh;
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
PODS:
|
||||
- boost-for-react-native (1.63.0)
|
||||
- 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):
|
||||
- boost-for-react-native
|
||||
- DoubleConversion
|
||||
@@ -57,6 +67,20 @@ PODS:
|
||||
- React-cxxreact (= 0.60.0)
|
||||
- React-jsi (= 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-Core (= 0.60.0)
|
||||
- React-RCTAnimation (0.60.0):
|
||||
@@ -81,10 +105,14 @@ PODS:
|
||||
- React-RCTWebSocket (0.60.0):
|
||||
- React-Core (= 0.60.0)
|
||||
- React-fishhook (= 0.60.0)
|
||||
- RNCAsyncStorage (1.6.1):
|
||||
- React
|
||||
- RNGestureHandler (1.3.0):
|
||||
- React
|
||||
- RNScreens (1.0.0-alpha.23):
|
||||
- React
|
||||
- RNSecureStorage (0.1.2):
|
||||
- React
|
||||
- RNVectorIcons (6.6.0):
|
||||
- React
|
||||
- yoga (0.60.0.React)
|
||||
@@ -101,6 +129,7 @@ DEPENDENCIES:
|
||||
- React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
|
||||
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
|
||||
- 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-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`)
|
||||
- React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`)
|
||||
@@ -111,14 +140,19 @@ DEPENDENCIES:
|
||||
- React-RCTText (from `../node_modules/react-native/Libraries/Text`)
|
||||
- React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
|
||||
- React-RCTWebSocket (from `../node_modules/react-native/Libraries/WebSocket`)
|
||||
- RNGestureHandler (from `/Users/mifi/Temporary Projects/eventment-app/node_modules/react-native-gesture-handler`)
|
||||
- RNScreens (from `/Users/mifi/Temporary Projects/eventment-app/node_modules/react-native-screens`)
|
||||
- RNVectorIcons (from `/Users/mifi/Temporary Projects/eventment-app/node_modules/react-native-vector-icons`)
|
||||
- "RNCAsyncStorage (from `/Users/mfitzpatrick/Documents/Personal/eventment-app/node_modules/@react-native-community/async-storage`)"
|
||||
- RNGestureHandler (from `/Users/mfitzpatrick/Documents/Personal/eventment-app/node_modules/react-native-gesture-handler`)
|
||||
- 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`)
|
||||
|
||||
SPEC REPOS:
|
||||
https://github.com/cocoapods/specs.git:
|
||||
- boost-for-react-native
|
||||
- FBSDKCoreKit
|
||||
- FBSDKLoginKit
|
||||
- FBSDKShareKit
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
DoubleConversion:
|
||||
@@ -143,6 +177,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native/ReactCommon/jsiexecutor"
|
||||
React-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:
|
||||
:path: "../node_modules/react-native/Libraries/ActionSheetIOS"
|
||||
React-RCTAnimation:
|
||||
@@ -163,18 +199,25 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native/Libraries/Vibration"
|
||||
React-RCTWebSocket:
|
||||
:path: "../node_modules/react-native/Libraries/WebSocket"
|
||||
RNCAsyncStorage:
|
||||
:path: "/Users/mfitzpatrick/Documents/Personal/eventment-app/node_modules/@react-native-community/async-storage"
|
||||
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:
|
||||
: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:
|
||||
:path: "/Users/mifi/Temporary Projects/eventment-app/node_modules/react-native-vector-icons"
|
||||
:path: "../node_modules/react-native-vector-icons"
|
||||
yoga:
|
||||
:path: "../node_modules/react-native/ReactCommon/yoga"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
|
||||
DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2
|
||||
FBSDKCoreKit: 39748deefe37a005f983722e433be2ddcdd83ae7
|
||||
FBSDKLoginKit: bc0329e10045789e8d40b12d4f15ddbd07a898e2
|
||||
FBSDKShareKit: f61d03f36ce4f1b379c7de7dbfe173662e68be44
|
||||
Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51
|
||||
glog: 1f3da668190260b06b429bb211bfbee5cd790c28
|
||||
React: 4b3c068e793e96672dcd186a2b572fac43e4b031
|
||||
@@ -185,6 +228,7 @@ SPEC CHECKSUMS:
|
||||
React-jsi: 8e128c4d0d8febc2977ef617d1c09bb54326069c
|
||||
React-jsiexecutor: 7a3554f703a58963ec80b860144ea0f0e9b910e1
|
||||
React-jsinspector: d4ed52225912efe0019bb7f1a225aec20f23049a
|
||||
react-native-fbsdk: 080f3bb23513c5b59b6528543206fe0d1436bb7e
|
||||
React-RCTActionSheet: b27ff3cf3a68f917c46d2b94abf938b625b96570
|
||||
React-RCTAnimation: 9e4708e5bd65fca8285ce7c0aa076f3f4fa5c2f8
|
||||
React-RCTBlob: 6eafcc3a24f33785692a7be24918ade607bc8719
|
||||
@@ -195,11 +239,13 @@ SPEC CHECKSUMS:
|
||||
React-RCTText: 685fca2e13b024271048e7e247ef24476f28a41e
|
||||
React-RCTVibration: 4ee1cf208ab17a50fafb1c16ffe28fe594a64e4f
|
||||
React-RCTWebSocket: fca087d583724aa0e5fef7d911f0f2a28d0f2736
|
||||
RNCAsyncStorage: 2e2e3feb9bdadc752a026703d8c4065ca912e75a
|
||||
RNGestureHandler: 5329a942fce3d41c68b84c2c2276ce06a696d8b0
|
||||
RNScreens: f28b48b8345f2f5f39ed6195518291515032a788
|
||||
RNSecureStorage: 24d433f7673a0daade43689de805933a70641731
|
||||
RNVectorIcons: 0bb4def82230be1333ddaeee9fcba45f0b288ed4
|
||||
yoga: 616fde658be980aa60a2158835170f3f9c2d04b4
|
||||
|
||||
PODFILE CHECKSUM: 26223b0d86281b1c70ae009fe120443ad6ad9bf9
|
||||
PODFILE CHECKSUM: 9a233bb7438f412e17eeb945a7c5f563882773de
|
||||
|
||||
COCOAPODS: 1.7.4
|
||||
COCOAPODS: 1.6.1
|
||||
|
||||
Reference in New Issue
Block a user