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
|
# CocoaPods
|
||||||
/ios/Pods/
|
/ios/Pods/
|
||||||
|
*.lock
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { List } from 'immutable';
|
import { List } from 'immutable';
|
||||||
|
|
||||||
import { fetchAuctionStatus as fetchActionStatusApi } from '../api/actionStatus.js';
|
import { fetchAuctionStatus as fetchActionStatusApi } from '../api/auctionStatus.js';
|
||||||
import { AUCTIONS_UPDATED } from '../constants/actionTypes.js';
|
import { AUCTIONS_UPDATED } from '../constants/actionTypes.js';
|
||||||
import { getActiveEventId } from '../selectors/activeEvent.js';
|
import { getActiveEventId } from '../selectors/activeEvent.js';
|
||||||
import { getAuthToken } from '../selectors/auth.js';
|
import { getAuthToken } from '../selectors/auth.js';
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
getNomAvailaibility,
|
getNomAvailaibility,
|
||||||
loginUser,
|
loginUser,
|
||||||
registerNewUser,
|
registerNewUser,
|
||||||
|
setNomDeBid as setNomDeBidApi,
|
||||||
} from '../api/profile.js';
|
} from '../api/profile.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -15,11 +16,22 @@ import {
|
|||||||
PROFILE_NOM_AVAILABLE,
|
PROFILE_NOM_AVAILABLE,
|
||||||
REGISTRATION_FAILURE,
|
REGISTRATION_FAILURE,
|
||||||
REGISTRATION_SUCCESS,
|
REGISTRATION_SUCCESS,
|
||||||
|
SET_NOM_FAILURE,
|
||||||
|
SET_NOM_SUCCESS,
|
||||||
UNSET_AUTH,
|
UNSET_AUTH,
|
||||||
UNSET_PROFILE,
|
UNSET_PROFILE,
|
||||||
UPDATE_PROFILE,
|
UPDATE_PROFILE,
|
||||||
} from '../constants/actionTypes.js';
|
} from '../constants/actionTypes.js';
|
||||||
|
|
||||||
|
import { getAuthToken } from '../selectors/auth.js';
|
||||||
|
import { getUserId } from '../selectors/profile.js';
|
||||||
|
|
||||||
|
|
||||||
|
const handleLoginFailure = (errors) => (dispatch) => {
|
||||||
|
dispatch(loginFailure(errors));
|
||||||
|
dispatch(unblockUI());
|
||||||
|
};
|
||||||
|
|
||||||
const isValidEmail = (payload) => ({
|
const isValidEmail = (payload) => ({
|
||||||
type: PROFILE_EMAIL_AVAILABLE,
|
type: PROFILE_EMAIL_AVAILABLE,
|
||||||
payload,
|
payload,
|
||||||
@@ -44,6 +56,16 @@ const logoutUser = () => ({
|
|||||||
type: DO_LOGOUT,
|
type: DO_LOGOUT,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const setNomFailure = ({ info }) => ({
|
||||||
|
type: SET_NOM_FAILURE,
|
||||||
|
payload: info,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const setNomSuccess = ({ nomDeBid }) => ({
|
||||||
|
type: SET_NOM_SUCCESS,
|
||||||
|
payload: nomDeBid,
|
||||||
|
});
|
||||||
|
|
||||||
const registrationFailure = (payload) => ({
|
const registrationFailure = (payload) => ({
|
||||||
type: REGISTRATION_FAILURE,
|
type: REGISTRATION_FAILURE,
|
||||||
payload,
|
payload,
|
||||||
@@ -71,13 +93,28 @@ export const checkEmailAvailability = (email) => (dispatch) => {};
|
|||||||
|
|
||||||
export const checkNomAvailability = (nomDeBid) => (dispatch) => {};
|
export const checkNomAvailability = (nomDeBid) => (dispatch) => {};
|
||||||
|
|
||||||
|
export const setNomDeBid = ({ nomDeBid }) => (dispatch, getState) => {
|
||||||
|
const id = getUserId(getState());
|
||||||
|
const auth = getAuthToken(getState());
|
||||||
|
|
||||||
|
setNomDeBidApi({ id, nomDeBid }, auth)
|
||||||
|
.then((result) => dispatch(setNomSuccess(result)))
|
||||||
|
.catch((err) => dispatch(setNomFailure(err)));
|
||||||
|
};
|
||||||
|
|
||||||
export const login = (username, password) => (dispatch) => {
|
export const login = (username, password) => (dispatch) => {
|
||||||
dispatch(blockUI());
|
dispatch(blockUI());
|
||||||
loginUser(username, password)
|
loginUser(username, password)
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
|
if (result.errors) {
|
||||||
|
dispatch(handleLoginFailure(result.errors));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
dispatch(loginSuccess(result));
|
dispatch(loginSuccess(result));
|
||||||
|
dispatch(unblockUI());
|
||||||
})
|
})
|
||||||
.catch((err) => dispatch(loginFailure(err)));
|
.catch((err) => dispatch(handleLoginFailure(err)));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const logout = () => (dispatch) => {
|
export const logout = () => (dispatch) => {
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ export const formatPostData = (body) => {
|
|||||||
return postData;
|
return postData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const formatJsonData = (body) => {
|
||||||
|
return JSON.stringify(body);
|
||||||
|
};
|
||||||
|
|
||||||
const parseQueryParamsString = (queryParams) => {
|
const parseQueryParamsString = (queryParams) => {
|
||||||
if (typeof queryParams !== 'string') {
|
if (typeof queryParams !== 'string') {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
constructUrl,
|
constructUrl,
|
||||||
formatPostData,
|
formatPostData,
|
||||||
|
formatJsonData,
|
||||||
parseQueryParams,
|
parseQueryParams,
|
||||||
request,
|
request,
|
||||||
unwrapJson,
|
unwrapJson,
|
||||||
@@ -24,6 +25,7 @@ export const API_ENDPOINTS = {
|
|||||||
PURCHASE_ITEM: '/sales',
|
PURCHASE_ITEM: '/sales',
|
||||||
|
|
||||||
// User/Profile
|
// User/Profile
|
||||||
|
LOGIN: '/auth',
|
||||||
USER_SIGNUP: '/signup',
|
USER_SIGNUP: '/signup',
|
||||||
USER_PROFILE: '/users/:user_id',
|
USER_PROFILE: '/users/:user_id',
|
||||||
|
|
||||||
@@ -45,11 +47,11 @@ const cacheBuster = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getEndpointUrl = (endpoint) => {
|
export const getEndpointUrl = (endpoint) => {
|
||||||
if (!endpoints[endpoint]) {
|
if (!API_ENDPOINTS[endpoint]) {
|
||||||
throw new Error('Invalid API endpoint specified');
|
throw new Error('Invalid API endpoint specified');
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${API_URL}${endpoints[endpoint]}`; //`${cacheBuster()}`;
|
return `${API_URL}${API_ENDPOINTS[endpoint]}`; //`${cacheBuster()}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const requestGet = (path, queryParams = [], requestOptions = {}) => {
|
export const requestGet = (path, queryParams = [], requestOptions = {}) => {
|
||||||
@@ -74,9 +76,15 @@ export const requestPost = (options) => {
|
|||||||
queryParams = [],
|
queryParams = [],
|
||||||
requestOptions = {},
|
requestOptions = {},
|
||||||
isFormattedPostData = false,
|
isFormattedPostData = false,
|
||||||
|
shouldUseFormData = false,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
const params = parseQueryParams(queryParams || []);
|
const params = parseQueryParams(queryParams || []);
|
||||||
|
let postData = isFormattedPostData ? body : null;
|
||||||
|
|
||||||
|
if (!postData) {
|
||||||
|
postData = shouldUseFormData ? formatPostData(body) : formatJsonData(body);
|
||||||
|
}
|
||||||
|
|
||||||
if (params === null) {
|
if (params === null) {
|
||||||
throw new Error('invalid queryParams');
|
throw new Error('invalid queryParams');
|
||||||
@@ -88,7 +96,7 @@ export const requestPost = (options) => {
|
|||||||
...DefaultRequestOptions,
|
...DefaultRequestOptions,
|
||||||
...requestOptions,
|
...requestOptions,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: isFormattedPostData ? body : formatPostData(body),
|
body: postData,
|
||||||
})
|
})
|
||||||
.then(validateResponse)
|
.then(validateResponse)
|
||||||
.then(unwrapJson);
|
.then(unwrapJson);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { API_ENDPOINTS, requestGet } from './index.js';
|
import { API_ENDPOINTS, requestGet, requestPost } from './index.js';
|
||||||
|
|
||||||
export const getEmailAvailability = (email) =>
|
export const getEmailAvailability = (email) =>
|
||||||
requestGet(`${API_ENDPOINTS.VALIDATE_SIGNUP_EMAIL}/&{encodeURI(email)}`);
|
requestGet(`${API_ENDPOINTS.VALIDATE_SIGNUP_EMAIL}/${encodeURI(email)}`);
|
||||||
|
|
||||||
export const getNomAvailaibility = (nomDeBid) =>
|
export const getNomAvailaibility = (nomDeBid) =>
|
||||||
requestGet(`${API_ENDPOINTS.VALIDATE_SIGNUP_NOM}/${encodeURI(nomDeBid)}`);
|
requestGet(`${API_ENDPOINTS.VALIDATE_SIGNUP_NOM}/${encodeURI(nomDeBid)}`);
|
||||||
@@ -17,3 +17,10 @@ export const registerNewUser = (user) =>
|
|||||||
path: API_ENDPOINTS.USER_SIGNUP,
|
path: API_ENDPOINTS.USER_SIGNUP,
|
||||||
body: { user },
|
body: { user },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const setNomDeBid = (id, auth) => (nomDeBid) =>
|
||||||
|
requestPost({
|
||||||
|
path: `${API_ENDPOINTS.SET_NOM}/${id}`,
|
||||||
|
body: { nomDeBid },
|
||||||
|
requestOptions: { Authorization: auth ? `Bearer ${auth}` : null },
|
||||||
|
});
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ import HeaderTitle from './HeaderTitle/HeaderTitle.container.js';
|
|||||||
import HeaderContentLeft from './HeaderContentLeft.container.js';
|
import HeaderContentLeft from './HeaderContentLeft.container.js';
|
||||||
import HeaderContentRight from './HeaderContentRight.container.js';
|
import HeaderContentRight from './HeaderContentRight.container.js';
|
||||||
|
|
||||||
import styles from './AppHeader.styles.js';
|
|
||||||
|
|
||||||
export default class AppHeader extends Component {
|
export default class AppHeader extends Component {
|
||||||
static get propTypes() {
|
static get propTypes() {
|
||||||
return {
|
return {
|
||||||
@@ -26,9 +24,9 @@ export default class AppHeader extends Component {
|
|||||||
return (
|
return (
|
||||||
<Header
|
<Header
|
||||||
placement="left"
|
placement="left"
|
||||||
leftComponent={<HeaderContentRight navigation={navigation} />}
|
|
||||||
centerComponent={<HeaderTitle navigation={navigation} />}
|
centerComponent={<HeaderTitle navigation={navigation} />}
|
||||||
rightComponent={<HeaderContentLeft navigation={navigation} />}
|
leftComponent={<HeaderContentLeft navigation={navigation} />}
|
||||||
|
rightComponent={<HeaderContentRight navigation={navigation} />}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { Text, TouchableOpacity, View } from 'react-native';
|
|
||||||
|
|
||||||
import BackIcon from './IconButtons/BackIcon.js';
|
import BackIcon from './IconButtons/BackIcon.js';
|
||||||
import EventsIcon from './IconButtons/EventsIcon.js';
|
import EventsIcon from './IconButtons/EventsIcon.js';
|
||||||
|
|
||||||
export default function HeaderContentLeft({ activeRoute, hasMultipleEvents, navigation }) {
|
export default function HeaderContentLeft({
|
||||||
|
activeRoute,
|
||||||
|
hasActiveEvent,
|
||||||
|
hasMultipleEvents,
|
||||||
|
navigation,
|
||||||
|
}) {
|
||||||
const _goBack = () => {
|
const _goBack = () => {
|
||||||
if (hasActiveEvent) {
|
if (hasActiveEvent) {
|
||||||
navigation.goBack();
|
navigation.goBack();
|
||||||
@@ -29,15 +32,20 @@ export default function HeaderContentLeft({ activeRoute, hasMultipleEvents, navi
|
|||||||
return <BackIcon action={_goBack} />;
|
return <BackIcon action={_goBack} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <EventsIcon action={hasMultipleEvents ? _showEvents : null} />;
|
if (hasMultipleEvents) {
|
||||||
|
return <EventsIcon action={_showEvents} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
HeaderContentLeft.propTypes = {
|
HeaderContentLeft.propTypes = {
|
||||||
activeRoute: PropTypes.string.isRequired,
|
activeRoute: PropTypes.string.isRequired,
|
||||||
hasActiveEvent: PropTypes.bool,
|
hasActiveEvent: PropTypes.bool,
|
||||||
navigation: PropTypes.func.isRequired,
|
hasMultipleEvents: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
HeaderContentLeft.defaultProps = {
|
HeaderContentLeft.defaultProps = {
|
||||||
hasActiveEvent: false,
|
hasActiveEvent: false,
|
||||||
|
hasMultipleEvents: false,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import HeaderContentRight from './HeaderContentRight.js';
|
|||||||
|
|
||||||
const matchStateToProps = (state, ownProps) => ({
|
const matchStateToProps = (state, ownProps) => ({
|
||||||
avatarUrl: getProfileAvatarUrl(state),
|
avatarUrl: getProfileAvatarUrl(state),
|
||||||
hideUserProfileButton: ownProps.navigation.state.routeName === 'Profile',
|
hideUserProfileButton:
|
||||||
|
['Profile', 'Register', 'SignInOrRegister'].indexOf(ownProps.navigation.state.routeName) >
|
||||||
|
-1,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export default function HeaderContentRight({ hideUserProfileButton, navigation }
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <UserProfileButton />;
|
return <UserProfileButton navigation={navigation} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
HeaderContentRight.propTypes = {
|
HeaderContentRight.propTypes = {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { hasActiveEvent } from '../../../../selectors/activeEvent.js';
|
||||||
import { getActiveEvent, getDefaultEvent } from '../../../../selectors/events.js';
|
import { getActiveEvent, getDefaultEvent } from '../../../../selectors/events.js';
|
||||||
|
|
||||||
import EventTitle from './EventTitle.js';
|
import EventTitle from './EventTitle.js';
|
||||||
|
|||||||
@@ -6,12 +6,16 @@ import { Text, TouchableOpacity, View } from 'react-native';
|
|||||||
import styles from './EventTitle.styles.js';
|
import styles from './EventTitle.styles.js';
|
||||||
|
|
||||||
export default function EventTitle({ action, date, end, name, start }) {
|
export default function EventTitle({ action, date, end, name, start }) {
|
||||||
const _generateEventTitle = () => (
|
const _generateEventTitle = () => {
|
||||||
<View style={styles.eventInfo}>
|
const whenString = `${date} | ${start} - ${end}`;
|
||||||
<Text style={styles.eventName}>{name}</Text>
|
|
||||||
<Text style={styles.eventDate}>{`${date} | ${start} - ${end}`}</Text>
|
return (
|
||||||
</View>
|
<View style={styles.eventInfo}>
|
||||||
);
|
<Text style={styles.eventName}>{name}</Text>
|
||||||
|
<Text style={styles.eventDate}>{whenString}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
if (action) {
|
if (action) {
|
||||||
return <TouchableOpacity onPress={action}>{_generateEventTitle()}</TouchableOpacity>;
|
return <TouchableOpacity onPress={action}>{_generateEventTitle()}</TouchableOpacity>;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
export default (styles = StyleSheet.create({
|
export default StyleSheet.create({
|
||||||
eventInfo: {
|
eventInfo: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
},
|
},
|
||||||
@@ -11,4 +11,4 @@ export default (styles = StyleSheet.create({
|
|||||||
eventDate: {
|
eventDate: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
},
|
||||||
}));
|
});
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { Text, TouchableOpacity, View } from 'react-native';
|
import { Text, TouchableOpacity } from 'react-native';
|
||||||
|
|
||||||
import EventTitle from './EventTitle/EventTitle.container.js';
|
import EventTitle from './EventTitle/EventTitle.container.js';
|
||||||
|
|
||||||
import styles from './HeaderTitle.styles.js';
|
import styles from './HeaderTitle.styles.js';
|
||||||
|
|
||||||
|
const STRINGS = {
|
||||||
|
EVENTS: 'Events',
|
||||||
|
PROFILE: 'Profile',
|
||||||
|
};
|
||||||
|
|
||||||
export default function HeaderTitle({
|
export default function HeaderTitle({
|
||||||
activeRoute,
|
activeRoute,
|
||||||
hasActiveEvent,
|
hasActiveEvent,
|
||||||
@@ -30,13 +35,13 @@ export default function HeaderTitle({
|
|||||||
if (activeRoute === 'Events') {
|
if (activeRoute === 'Events') {
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity onPress={_goBack}>
|
<TouchableOpacity onPress={_goBack}>
|
||||||
<Text style={styles.screenHeader}>Profile</Text>
|
<Text style={styles.screenHeader}>{STRINGS.EVENTS}</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activeRoute === 'Profile') {
|
if (activeRoute === 'Profile') {
|
||||||
return <Text style={styles.screenHeader}>Profile</Text>;
|
return <Text style={styles.screenHeader}>{STRINGS.PROFILE}</Text>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <EventTitle action={hasMultipleEvents ? _showEvents : null} />;
|
return <EventTitle action={hasMultipleEvents ? _showEvents : null} />;
|
||||||
@@ -46,7 +51,6 @@ HeaderTitle.propTypes = {
|
|||||||
activeRoute: PropTypes.string.isRequired,
|
activeRoute: PropTypes.string.isRequired,
|
||||||
hasActiveEvent: PropTypes.bool,
|
hasActiveEvent: PropTypes.bool,
|
||||||
hasMultipleEvents: PropTypes.bool.isRequired,
|
hasMultipleEvents: PropTypes.bool.isRequired,
|
||||||
navigation: PropTypes.func.isRequired,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
HeaderTitle.defaultProps = {
|
HeaderTitle.defaultProps = {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
export default (styles = StyleSheet.create({
|
export default StyleSheet.create({
|
||||||
filterBar: {
|
filterBar: {
|
||||||
backgroundColor: '#0F0',
|
backgroundColor: '#0F0',
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
@@ -11,4 +11,4 @@ export default (styles = StyleSheet.create({
|
|||||||
view: {
|
view: {
|
||||||
flex: 2,
|
flex: 2,
|
||||||
},
|
},
|
||||||
}));
|
});
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { TouchableOpacity } from 'react-native';
|
import { TouchableOpacity } from 'react-native';
|
||||||
import { Icon } from 'react-native-elements';
|
import Icon from 'react-native-vector-icons';
|
||||||
|
|
||||||
export default function BackIcon({ action }) {
|
export default function BackIcon({ action }) {
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity onPress={action}>
|
<TouchableOpacity onPress={action}>
|
||||||
<Icon name="ei-chevron-left" type="evilicons" size={28} />;
|
<Icon name="ei-chevron-left" type="evilicons" size={28} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { TouchableOpacity } from 'react-native';
|
|||||||
import { Icon } from 'react-native-elements';
|
import { Icon } from 'react-native-elements';
|
||||||
|
|
||||||
export default function EventsIcon({ action }) {
|
export default function EventsIcon({ action }) {
|
||||||
const renderEventsIcon = () => <Icon name="ei-calendar" type="evilicons" size={28} />;
|
const renderEventsIcon = () => <Icon name="calendar" type="evilicon" size={28} />;
|
||||||
|
|
||||||
if (action) {
|
if (action) {
|
||||||
return <TouchableOpacity onPress={action}>{renderEventsIcon()}</TouchableOpacity>;
|
return <TouchableOpacity onPress={action}>{renderEventsIcon()}</TouchableOpacity>;
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { getProfileAvatarUrl } from '../../../selectors/profile.js';
|
import {
|
||||||
|
getProfileAvatarUrl,
|
||||||
|
getUserInitials,
|
||||||
|
isRegisteredAccount,
|
||||||
|
} from '../../../selectors/profile.js';
|
||||||
|
|
||||||
import UserProfileButton from './UserProfileButton.js';
|
import UserProfileButton from './UserProfileButton.js';
|
||||||
|
|
||||||
const matchStateToProps = (state) => ({
|
const matchStateToProps = (state) => ({
|
||||||
avatarUrl: getProfileAvatarUrl(state),
|
avatarUrl: getProfileAvatarUrl(state),
|
||||||
|
initials: getUserInitials(state),
|
||||||
|
isRegisteredAccount: isRegisteredAccount(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
|
|||||||
@@ -2,22 +2,33 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { Image, TouchableOpacity, View } from 'react-native';
|
import { Image, TouchableOpacity, View } from 'react-native';
|
||||||
import { Icon } from 'react-native-elements';
|
import { Avatar, Icon } from 'react-native-elements';
|
||||||
|
|
||||||
import styles from './UserProfileButton.styles.js';
|
import styles from './UserProfileButton.styles.js';
|
||||||
|
|
||||||
export default function UserProfileButton({ avatarUrl, navigation }) {
|
export default function UserProfileButton({
|
||||||
|
avatarUrl,
|
||||||
|
initials,
|
||||||
|
isRegisteredAccount,
|
||||||
|
navigation,
|
||||||
|
}) {
|
||||||
const _goToProfile = () => {
|
const _goToProfile = () => {
|
||||||
navigation.navigate('Profile');
|
if (isRegisteredAccount) {
|
||||||
return false;
|
navigation.navigate('Profile');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
navigation.navigate('SignInOrRegister');
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity onPress={_goToProfile}>
|
<TouchableOpacity onPress={_goToProfile}>
|
||||||
{avatarUrl !== null ? (
|
{isRegisteredAccount !== null ? (
|
||||||
<View style={styles.avatarWrap}>
|
avatarUrl !== null ? (
|
||||||
<Image source={{ uri: avatarUrl }} />
|
<Avatar source={{ uri: avatarUrl }} />
|
||||||
</View>
|
) : (
|
||||||
|
<Avatar title={initials} />
|
||||||
|
)
|
||||||
) : (
|
) : (
|
||||||
<Icon name="ei-user" type="evilicons" size={28} />
|
<Icon name="ei-user" type="evilicons" size={28} />
|
||||||
)}
|
)}
|
||||||
@@ -27,8 +38,12 @@ export default function UserProfileButton({ avatarUrl, navigation }) {
|
|||||||
|
|
||||||
UserProfileButton.propTypes = {
|
UserProfileButton.propTypes = {
|
||||||
avatarUrl: PropTypes.string,
|
avatarUrl: PropTypes.string,
|
||||||
|
initials: PropTypes.string,
|
||||||
|
isRegisteredAccount: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
UserProfileButton.propTypes = {
|
UserProfileButton.defaultProps = {
|
||||||
avatarUrl: null,
|
avatarUrl: null,
|
||||||
|
initials: null,
|
||||||
|
isRegisteredAccount: false,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { connect } from 'react-redux';
|
|||||||
|
|
||||||
import { placeBid } from '../../actions/auction.js';
|
import { placeBid } from '../../actions/auction.js';
|
||||||
|
|
||||||
import AuctionListItem from '../../components/Auction/AuctionListItem.js';
|
import AuctionListItem from './AuctionListItem.js';
|
||||||
|
|
||||||
const mapStateToProps = (state, ownProps) => {
|
const mapStateToProps = (state, ownProps) => {
|
||||||
const { item } = ownProps;
|
const { item } = ownProps;
|
||||||
@@ -27,5 +27,5 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
null,
|
mapDispatchToProps,
|
||||||
)(AuctionListItem);
|
)(AuctionListItem);
|
||||||
@@ -5,8 +5,8 @@ import { StyleSheet, TouchableOpacity, Text, Image, View } from 'react-native';
|
|||||||
|
|
||||||
import GallerySwiper from 'react-native-gallery-swiper';
|
import GallerySwiper from 'react-native-gallery-swiper';
|
||||||
|
|
||||||
import AuctionPriceAndBidCount from '../../containers/Auction/AuctionPriceAndBidCount.js';
|
import AuctionPriceAndBidCount from './AuctionPriceAndBidCount.container.js';
|
||||||
import BidStatus from '../../containers/Auction/BidStatus.js';
|
import BidStatus from './BidStatus.container.js';
|
||||||
|
|
||||||
import { ITEM_TYPES } from '../../constants/constants.js';
|
import { ITEM_TYPES } from '../../constants/constants.js';
|
||||||
import { formatPrice, getAuctionTime } from '../../library/helpers.js';
|
import { formatPrice, getAuctionTime } from '../../library/helpers.js';
|
||||||
@@ -83,28 +83,28 @@ export default class AuctionListItem extends Component {
|
|||||||
)}
|
)}
|
||||||
<View style={styles.rowText}>
|
<View style={styles.rowText}>
|
||||||
{type === ITEM_TYPES.AUCTION && <BidStatus itemId={id} />}
|
{type === ITEM_TYPES.AUCTION && <BidStatus itemId={id} />}
|
||||||
<Text style={styles.title} numberOfLines={2} ellipsizeMode={'tail'}>
|
<Text style={styles.title} numberOfLines={2} ellipsizeMode="tail">
|
||||||
{title}
|
{title}
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={styles.subtitle} numberOfLines={1} ellipsizeMode={'tail'}>
|
<Text style={styles.subtitle} numberOfLines={1} ellipsizeMode="tail">
|
||||||
{subtitle}
|
{subtitle}
|
||||||
</Text>
|
</Text>
|
||||||
{donor && (
|
{donor && (
|
||||||
<Text style={styles.donor} numberOfLines={1} ellipsizeMode={'tail'}>
|
<Text style={styles.donor} numberOfLines={1} ellipsizeMode="tail">
|
||||||
{donor}
|
{donor}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
{type === ITEM_TYPES.AUCTION ? (
|
{type === ITEM_TYPES.AUCTION ? (
|
||||||
<AuctionPriceAndBidCount itemId={id} />
|
<AuctionPriceAndBidCount itemId={id} />
|
||||||
) : (
|
) : (
|
||||||
<Text style={styles.price} numberOfLines={1} ellipsizeMode={'tail'}>
|
<Text style={styles.price} numberOfLines={1} ellipsizeMode="tail">
|
||||||
{formatPrice(startingPrice)}
|
{formatPrice(startingPrice)}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
<Text style={styles.timeline} numberOfLines={1}>
|
<Text style={styles.timeline} numberOfLines={1}>
|
||||||
{this._getBidTime()}
|
{this._getBidTime()}
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={styles.description} numberOfLines={3} ellipsizeMode={'tail'}>
|
<Text style={styles.description} numberOfLines={3} ellipsizeMode="tail">
|
||||||
{description}
|
{description}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { connect } from 'react-redux';
|
|||||||
|
|
||||||
import { getItemBidCount, getItemPrice } from '../../selectors/auctions.js';
|
import { getItemBidCount, getItemPrice } from '../../selectors/auctions.js';
|
||||||
|
|
||||||
import AuctionPriceAndBidCount from '../../components/Auction/AuctionPriceAndBidCount.js';
|
import AuctionPriceAndBidCount from './AuctionPriceAndBidCount.js';
|
||||||
|
|
||||||
function mapStateToProps(state, ownProps) {
|
function mapStateToProps(state, ownProps) {
|
||||||
const { itemId } = ownProps;
|
const { itemId } = ownProps;
|
||||||
@@ -6,9 +6,11 @@ import { formatPrice } from '../../library/helpers.js';
|
|||||||
import { StyleSheet, Text } from 'react-native';
|
import { StyleSheet, Text } from 'react-native';
|
||||||
|
|
||||||
const AuctionPriceAndBidCount = ({ bidCount, currentPrice }) => {
|
const AuctionPriceAndBidCount = ({ bidCount, currentPrice }) => {
|
||||||
|
const _getPriceAndBidString = () => `${formatPrice(currentPrice)} (${bidCount} bids)`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text style={styles.currentPriceAndBidCount} numberOfLines={1}>
|
<Text style={styles.currentPriceAndBidCount} numberOfLines={1}>
|
||||||
{`${formatPrice(currentPrice)} (${bidCount} bids)`}
|
{_getPriceAndBidString()}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { connect } from 'react-redux';
|
|||||||
|
|
||||||
import { isBiddingItem, isWinningItem } from '../../selectors/auctions.js';
|
import { isBiddingItem, isWinningItem } from '../../selectors/auctions.js';
|
||||||
|
|
||||||
import AuctionPriceAndBidCount from '../../components/Auction/BidStatus.js';
|
import AuctionPriceAndBidCount from './BidStatus.js';
|
||||||
|
|
||||||
function mapStateToProps(state, ownProps) {
|
function mapStateToProps(state, ownProps) {
|
||||||
const { itemId } = ownProps;
|
const { itemId } = ownProps;
|
||||||
@@ -9,7 +9,7 @@ const BidStatus = ({ isBidding, isWinning }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const statusBarStyle = isWinning
|
const statusBarStyle = isWinning
|
||||||
? [styles.bidStatus, styes.isWinning]
|
? [styles.bidStatus, styles.isWinning]
|
||||||
: [styles.bidStatus, styles.isOutbid];
|
: [styles.bidStatus, styles.isOutbid];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -3,10 +3,15 @@ import PropTypes from 'prop-types';
|
|||||||
|
|
||||||
import { StyleSheet, Text, View } from 'react-native';
|
import { StyleSheet, Text, View } from 'react-native';
|
||||||
|
|
||||||
|
const STRINGS = {
|
||||||
|
FILTER: 'Filter',
|
||||||
|
VIEW: 'View',
|
||||||
|
};
|
||||||
|
|
||||||
const FilterBar = ({ changeFilterer, changeViewMode, filterMode, viewMode }) => (
|
const FilterBar = ({ changeFilterer, changeViewMode, filterMode, viewMode }) => (
|
||||||
<View style={styles.filterBar}>
|
<View style={styles.filterBar}>
|
||||||
<Text style={styles.filter}>Filter</Text>
|
<Text style={styles.filter}>{STRINGS.FILTER}</Text>
|
||||||
<Text style={styles.view}>View</Text>
|
<Text style={styles.view}>{STRINGS.VIEW}</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { setActiveEvent } from '../../actions/events.js';
|
import { setActiveEvent } from '../../actions/events.js';
|
||||||
import EventListItem from '../../components/Events/EventListItem.js';
|
import EventListItem from './EventListItem.js';
|
||||||
|
|
||||||
const mapStateToProps = (state, ownProps) => {
|
const mapStateToProps = (state, ownProps) => {
|
||||||
const { event } = ownProps;
|
const { event } = ownProps;
|
||||||
@@ -27,5 +27,5 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
null,
|
mapDispatchToProps,
|
||||||
)(AuctionListItem);
|
)(EventListItem);
|
||||||
@@ -33,24 +33,28 @@ export default class EventListItem extends Component {
|
|||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
this._viewEventDetail = this._viewEventDetail.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
_viewEventDetail = () => {
|
getTimeString() {
|
||||||
|
const { end, start } = this.props;
|
||||||
|
return `${start} - ${end}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
_viewEventDetail() {
|
||||||
this.props.setActiveEvent(this.props.id);
|
this.props.setActiveEvent(this.props.id);
|
||||||
this.props.navigation.navigate('Event');
|
this.props.navigation.navigate('Event');
|
||||||
};
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { date, description, end, name, start } = this.props;
|
const { date, description, name } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity onPress={this._viewEventDetail}>
|
<TouchableOpacity onPress={this._viewEventDetail}>
|
||||||
<View style={styles.rowContainer}>
|
<View style={styles.rowContainer}>
|
||||||
<Text>{name}</Text>
|
<Text>{name}</Text>
|
||||||
<Text>{date}</Text>
|
<Text>{date}</Text>
|
||||||
<Text>
|
<Text>{this.getTimeString()}</Text>
|
||||||
{start} - {end}
|
|
||||||
</Text>
|
|
||||||
<Text>{description}</Text>
|
<Text>{description}</Text>
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|||||||
@@ -3,54 +3,51 @@ import PropTypes from 'prop-types';
|
|||||||
|
|
||||||
import { Button, TextInput, View } from 'react-native';
|
import { Button, TextInput, View } from 'react-native';
|
||||||
|
|
||||||
|
import styles from './Login.styles.js';
|
||||||
|
|
||||||
export default function LocalLogin({ doLoginAction }) {
|
export default function LocalLogin({ doLoginAction }) {
|
||||||
|
const [enabled, setEnableSubmit] = useState(false);
|
||||||
|
const [password, setPassword] = useState(null);
|
||||||
|
const [username, setUsername] = useState(null);
|
||||||
|
|
||||||
const [ enabled, setEnableSubmit ] = useState(false);
|
const _handleLoginSubmit = () => {
|
||||||
const [ password, setPassword ] = useState(null);
|
doLoginAction(username, password);
|
||||||
const [ username, setUsername ] = useState(null);
|
};
|
||||||
|
|
||||||
const _handleLoginSubmit = () => {
|
const _updateState = (field, value) => {
|
||||||
doLoginAction(username, password);
|
if (field === 'username') {
|
||||||
};
|
setUsername(value);
|
||||||
|
}
|
||||||
|
|
||||||
const _updateState = (field, value) => {
|
if (field === 'password') {
|
||||||
if (field === 'username') {
|
setPassword(value);
|
||||||
setUsername(value);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (field === 'password') {
|
if (!!username && !!password) {
|
||||||
setPassword(value);
|
setEnableSubmit(true);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (!!username && !!password) {
|
return (
|
||||||
setEnableSubmit(true);
|
<View style={styles.loginWrap}>
|
||||||
}
|
<TextInput
|
||||||
};
|
keyboardType="email-address"
|
||||||
|
onChangeText={(text) => _updateState('username', text)}
|
||||||
return (
|
placeholder="email"
|
||||||
<View style={styles.loginWrap}>
|
style={styles.textInput}
|
||||||
<TextInput
|
value={username}
|
||||||
keyboardType="email-address"
|
/>
|
||||||
onChangeText={(text) => _updateState('username', text)}
|
<TextInput
|
||||||
placeholder="email"
|
enablesReturnKeyAutomatically
|
||||||
style={{height: 40}}
|
onChangeText={(text) => _updateState('password', text)}
|
||||||
value={username}
|
placeholder="password"
|
||||||
/>
|
secureTextEntry
|
||||||
<TextInput
|
style={styles.textInput}
|
||||||
enablesReturnKeyAutomatically
|
value={password}
|
||||||
onChangeText={(text) => _updateState('password', text)}
|
/>
|
||||||
placeholder="password"
|
<Button disabled={!enabled} onPress={_handleLoginSubmit} title="Login" />
|
||||||
secureTextEntry
|
</View>
|
||||||
style={{height: 40}}
|
);
|
||||||
value={password}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
disabled={!enabled}
|
|
||||||
onPress={_handleLoginSubmit}
|
|
||||||
title="Login"
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalLogin.propTypes = {
|
LocalLogin.propTypes = {
|
||||||
|
|||||||
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 UPDATE_PROFILE = 'UPDATE_PROFILE';
|
||||||
|
|
||||||
export const SET_NOM_DE_BID = 'SET_NOM_DE_BID';
|
export const SET_NOM_DE_BID = 'SET_NOM_DE_BID';
|
||||||
|
export const SET_NOM_FAILURE = 'SET_NOM_FAILURE';
|
||||||
|
export const SET_NOM_SUCCESS = 'SET_NOM_SUCCESS';
|
||||||
|
|
||||||
export const SET_PASSWORD = 'SET_PASSWORD';
|
export const SET_PASSWORD = 'SET_PASSWORD';
|
||||||
|
|
||||||
export const ADD_PAYMENT_DATA = 'ADD_PAYMENT_DATA';
|
export const ADD_PAYMENT_DATA = 'ADD_PAYMENT_DATA';
|
||||||
|
|||||||
@@ -51,3 +51,11 @@ export const API_ENDPOINTS = {
|
|||||||
export const PERMISSIONS = {
|
export const PERMISSIONS = {
|
||||||
FACEBOOK: ['email', 'public_profile'],
|
FACEBOOK: ['email', 'public_profile'],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const PHONE_TYPES = [
|
||||||
|
{ label: 'home', value: 'home' },
|
||||||
|
{ label: 'mobile', value: 'mobile' },
|
||||||
|
{ label: 'work', value: 'work' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const PHONE_TYPE_DEFAULT = PHONE_TYPES[0].value;
|
||||||
|
|||||||
@@ -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}`;
|
return `${this.firstName} ${this.lastName}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get initials() {
|
||||||
|
const firstInitial = this.firstName ? this.firstName.substring(0, 1) : null;
|
||||||
|
const lastInitial = this.firstName ? this.firstName.substring(0, 1) : null;
|
||||||
|
|
||||||
|
if (!firstInitial && !lastInitial) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${firstInitial || ''}${lastInitial || ''}`;
|
||||||
|
}
|
||||||
|
|
||||||
get isRegisteredAccount() {
|
get isRegisteredAccount() {
|
||||||
return (
|
return (
|
||||||
this.hasLinkedApple ||
|
this.hasLinkedApple ||
|
||||||
|
|||||||
@@ -13,9 +13,20 @@ import ImageDetail from './screens/ImageDetail.js';
|
|||||||
import Item from './screens/Item.js';
|
import Item from './screens/Item.js';
|
||||||
import Marketplace from './screens/Marketplace.js';
|
import Marketplace from './screens/Marketplace.js';
|
||||||
import Profile from './screens/Profile.container.js';
|
import Profile from './screens/Profile.container.js';
|
||||||
import Register from './screens/Register.js';
|
import Register from './screens/Register.container.js';
|
||||||
import SignInOrRegister from './screens/SignInOrRegister.js';
|
import SignInOrRegister from './screens/SignInOrRegister.js';
|
||||||
|
|
||||||
|
const tabBarVisibility = ({ navigation }) => {
|
||||||
|
let tabBarVisible = true;
|
||||||
|
if (navigation.state.index > 0) {
|
||||||
|
tabBarVisible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
tabBarVisible,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const SignInOrRegisterStack = createStackNavigator({
|
export const SignInOrRegisterStack = createStackNavigator({
|
||||||
SignInOrRegister: {
|
SignInOrRegister: {
|
||||||
screen: SignInOrRegister,
|
screen: SignInOrRegister,
|
||||||
@@ -35,6 +46,33 @@ export const SignInOrRegisterStack = createStackNavigator({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
SignInOrRegisterStack.navigationOptions = tabBarVisibility;
|
||||||
|
|
||||||
|
export const ProfileStack = createStackNavigator({
|
||||||
|
Profile: {
|
||||||
|
screen: Profile,
|
||||||
|
navigationOptions: ({ navigation }) => ({
|
||||||
|
header: <AppHeader navigation={navigation} />,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
SignInOrRegister: {
|
||||||
|
screen: SignInOrRegister,
|
||||||
|
navigationOptions: ({ navigation }) => ({
|
||||||
|
header: null,
|
||||||
|
gesturesEnabled: false,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Register: {
|
||||||
|
screen: Register,
|
||||||
|
navigationOptions: ({ navigation }) => ({
|
||||||
|
header: null,
|
||||||
|
gesturesEnabled: false,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
ProfileStack.navigationOptions = tabBarVisibility;
|
||||||
|
|
||||||
export const AuctionStack = createStackNavigator({
|
export const AuctionStack = createStackNavigator({
|
||||||
Auction: {
|
Auction: {
|
||||||
screen: Auction,
|
screen: Auction,
|
||||||
@@ -60,6 +98,8 @@ export const AuctionStack = createStackNavigator({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AuctionStack.navigationOptions = tabBarVisibility;
|
||||||
|
|
||||||
export const BazaarStack = createStackNavigator({
|
export const BazaarStack = createStackNavigator({
|
||||||
Bazaar: {
|
Bazaar: {
|
||||||
screen: Marketplace,
|
screen: Marketplace,
|
||||||
@@ -93,12 +133,13 @@ export const BazaarStack = createStackNavigator({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
BazaarStack.navigationOptions = tabBarVisibility;
|
||||||
|
|
||||||
export const EventsStack = createStackNavigator({
|
export const EventsStack = createStackNavigator({
|
||||||
Events: {
|
Events: {
|
||||||
screen: Events,
|
screen: Events,
|
||||||
navigationOptions: ({ navigation }) => ({
|
navigationOptions: ({ navigation }) => ({
|
||||||
header: <AppHeader navigation={navigation} />,
|
header: <AppHeader navigation={navigation} />,
|
||||||
tabBarVisible: false,
|
|
||||||
gesturesEnabled: false,
|
gesturesEnabled: false,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -106,12 +147,13 @@ export const EventsStack = createStackNavigator({
|
|||||||
screen: Event,
|
screen: Event,
|
||||||
navigationOptions: ({ navigation }) => ({
|
navigationOptions: ({ navigation }) => ({
|
||||||
header: <AppHeader navigation={navigation} />,
|
header: <AppHeader navigation={navigation} />,
|
||||||
tabBarVisible: false,
|
|
||||||
gesturesEnabled: false,
|
gesturesEnabled: false,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
EventsStack.navigationOptions = tabBarVisibility;
|
||||||
|
|
||||||
export const Tabs = createBottomTabNavigator({
|
export const Tabs = createBottomTabNavigator({
|
||||||
Event: {
|
Event: {
|
||||||
screen: EventsStack,
|
screen: EventsStack,
|
||||||
@@ -141,52 +183,12 @@ export const Tabs = createBottomTabNavigator({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Profile: {
|
Profile: {
|
||||||
screen: Profile,
|
screen: ProfileStack,
|
||||||
navigationOptions: {
|
navigationOptions: {
|
||||||
tabBarLabel: 'Profile',
|
tabBarLabel: 'Profile',
|
||||||
tabBarIcon: ({ tintColor }) => (
|
tabBarIcon: ({ tintColor }) => (
|
||||||
<Icon name="ios-person" type="font-awesome" size={28} color={tintColor} />
|
<Icon name="user" type="evilicon" size={28} color={tintColor} />
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
}, { initialRouteName: 'Event' });
|
||||||
|
|
||||||
export const createRootNavigator = () => {
|
|
||||||
return StackNavigator(
|
|
||||||
{
|
|
||||||
AuctionStack: {
|
|
||||||
screen: AuctionStack,
|
|
||||||
navigationOptions: {
|
|
||||||
gesturesEnabled: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
BazaarStack: {
|
|
||||||
screen: BazaarStack,
|
|
||||||
navigationOptions: {
|
|
||||||
gesturesEnabled: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
EventsStack: {
|
|
||||||
screen: EventsStack,
|
|
||||||
navigationOptions: {
|
|
||||||
gesturesEnabled: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
SignInOrRegisterStack: {
|
|
||||||
screen: SignInOrRegister,
|
|
||||||
navigationOptions: {
|
|
||||||
gesturesEnabled: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Tabs: {
|
|
||||||
screen: Tabs,
|
|
||||||
navigationOptions: {
|
|
||||||
gesturesEnabled: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
mode: 'modal',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -7,17 +7,16 @@ import { getAuctionItemsAsList } from '../selectors/items.js';
|
|||||||
|
|
||||||
import Auction from './Auction.js';
|
import Auction from './Auction.js';
|
||||||
|
|
||||||
const matchStateToProps = (state) => {
|
const changeViewMode = () => true;
|
||||||
const items = getAuctionItemsAsList(state);
|
|
||||||
console.log('items:', items);
|
|
||||||
|
|
||||||
return { items };
|
const matchStateToProps = (state) => ({
|
||||||
};
|
items: getAuctionItemsAsList(state),
|
||||||
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
changeViewMode: (mode) => dispatch(changeViewMode(mode)),
|
changeViewMode: (mode) => dispatch(changeViewMode(mode)),
|
||||||
fetchItems: () => dispatch(fetchItems(dispatch)),
|
fetchItems: () => dispatch(fetchItems()),
|
||||||
fetchStatus: () => dispatch(fetchAuctionStatus(dispatch)),
|
fetchStatus: () => dispatch(fetchAuctionStatus()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { FlatList, Text, View } from 'react-native';
|
|||||||
import { SORT_MODES, AUCTION_VIEW_MODES } from '../constants/constants.js';
|
import { SORT_MODES, AUCTION_VIEW_MODES } from '../constants/constants.js';
|
||||||
|
|
||||||
import FilterBar from '../components/Auction/FilterBar.js';
|
import FilterBar from '../components/Auction/FilterBar.js';
|
||||||
import AuctionListItem from '../containers/Auction/AuctionListItem.js';
|
import AuctionListItem from '../components/Auction/AuctionListItem.container.js';
|
||||||
|
|
||||||
import styles from './Auction.styles.js';
|
import styles from './Auction.styles.js';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
export default (styles = StyleSheet.create({
|
export default StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
@@ -14,4 +14,4 @@ export default (styles = StyleSheet.create({
|
|||||||
alignItems: 'stretch',
|
alignItems: 'stretch',
|
||||||
justifyContent: 'flex-start',
|
justifyContent: 'flex-start',
|
||||||
},
|
},
|
||||||
}));
|
});
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ import { StyleSheet, Text, View } from 'react-native';
|
|||||||
|
|
||||||
export default class Checkout extends Component {
|
export default class Checkout extends Component {
|
||||||
render() {
|
render() {
|
||||||
|
const title = 'Checkout';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Text style={styles.title}>Checkout</Text>
|
<Text style={styles.title}>{title}</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export default class Event extends Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Text style={styles.title}>Event</Text>
|
<Text style={styles.title}>{title}</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
export default (styles = StyleSheet.create({
|
export default StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
@@ -12,4 +12,4 @@ export default (styles = StyleSheet.create({
|
|||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
margin: 10,
|
margin: 10,
|
||||||
},
|
},
|
||||||
}));
|
});
|
||||||
|
|||||||
@@ -6,10 +6,9 @@ import { getEventsAsList } from '../selectors/events.js';
|
|||||||
|
|
||||||
import Events from './Events.js';
|
import Events from './Events.js';
|
||||||
|
|
||||||
const matchStateToProps = (state) => {
|
const matchStateToProps = (state) => ({
|
||||||
const events = getEventsAsList(state);
|
events: getEventsAsList(state),
|
||||||
return { events };
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
fetchEvents: () => dispatch(fetchEvents()),
|
fetchEvents: () => dispatch(fetchEvents()),
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ import { List } from 'immutable';
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { FlatList, StyleSheet, Text, View } from 'react-native';
|
import { ActivityIndicator, FlatList, StyleSheet, View } from 'react-native';
|
||||||
|
|
||||||
import EventListItem from '../components/Events/EventListItem.js';
|
import EventListItem from '../components/Events/EventListItem.container.js';
|
||||||
|
|
||||||
export default class Events extends Component {
|
export default class Events extends Component {
|
||||||
static get propTypes() {
|
static get propTypes() {
|
||||||
return {
|
return {
|
||||||
events: PropTypes.instanceOf(List),
|
events: PropTypes.oneOfType([PropTypes.array, PropTypes.instanceOf(List)]),
|
||||||
fetchEvents: PropTypes.func.isRequired,
|
fetchEvents: PropTypes.func.isRequired,
|
||||||
setActiveEvent: PropTypes.func.isRequired,
|
setActiveEvent: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
@@ -17,36 +17,32 @@ export default class Events extends Component {
|
|||||||
|
|
||||||
static get defaultProps() {
|
static get defaultProps() {
|
||||||
return {
|
return {
|
||||||
events: new List(),
|
events: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
this._renderEventListItem = this._renderEventListItem.bind(this);
|
||||||
this._setActiveEvent = this.setActiveEvent.bind(this);
|
this._setActiveEvent = this.setActiveEvent.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.props.fetchEvents();
|
|
||||||
}
|
|
||||||
|
|
||||||
setActiveEvent(eventId) {
|
setActiveEvent(eventId) {
|
||||||
this.props.setActiveEvent(eventId);
|
this.props.setActiveEvent(eventId);
|
||||||
}
|
}
|
||||||
|
|
||||||
_keyExtractor = (event, index) => `${event.id}_${index}`;
|
_keyExtractor = (event, index) => `${event.id}_${index}`;
|
||||||
|
|
||||||
_renderEventListItem = ({ event }) => (
|
_renderEventListItem({ event }) {
|
||||||
<EventListItem {...event} setActiveEvent={this.setActiveEvent} />
|
return <EventListItem event={event} setActiveEvent={this.setActiveEvent} />;
|
||||||
);
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { events } = this.props;
|
const { events } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
{events.size > 0 && (
|
{events.size > 0 ? (
|
||||||
<FlatList
|
<FlatList
|
||||||
data={events}
|
data={events}
|
||||||
keyExtractor={this._keyExtractor}
|
keyExtractor={this._keyExtractor}
|
||||||
@@ -54,6 +50,8 @@ export default class Events extends Component {
|
|||||||
contentContainerStyle={styles.eventListContentContainer}
|
contentContainerStyle={styles.eventListContentContainer}
|
||||||
style={styles.eventList}
|
style={styles.eventList}
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<ActivityIndicator animating={true} size="large" />
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
@@ -67,5 +65,8 @@ const styles = StyleSheet.create({
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
backgroundColor: '#F5FCFF',
|
backgroundColor: '#F5FCFF',
|
||||||
},
|
},
|
||||||
eventListContentContainer: {},
|
eventListContentContainer: {
|
||||||
|
alignItems: 'stretch',
|
||||||
|
justifyContent: 'flex-start',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ import { StyleSheet, Text, View } from 'react-native';
|
|||||||
|
|
||||||
export default class ImageDetail extends Component {
|
export default class ImageDetail extends Component {
|
||||||
render() {
|
render() {
|
||||||
|
const title = 'Item';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Text style={styles.title}>Item</Text>
|
<Text style={styles.title}>{title}</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ import { StyleSheet, Text, View } from 'react-native';
|
|||||||
|
|
||||||
export default class Item extends Component {
|
export default class Item extends Component {
|
||||||
render() {
|
render() {
|
||||||
|
const title = 'Item';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Text style={styles.title}>Item</Text>
|
<Text style={styles.title}>{title}</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 { SORT_MODES, AUCTION_VIEW_MODES } from '../constants/constants.js';
|
||||||
|
|
||||||
import FilterBar from '../components/Auction/FilterBar.js';
|
import FilterBar from '../components/Auction/FilterBar.js';
|
||||||
import AuctionListItem from '../containers/Auction/AuctionListItem.js';
|
import AuctionListItem from '../components/Auction/AuctionListItem.container.js';
|
||||||
|
|
||||||
//import styles from './Marketplace.styles.js';
|
import styles from './Auction.styles.js';
|
||||||
|
|
||||||
export default class Marketplace extends Component {
|
export default class Marketplace extends Component {
|
||||||
static get propTypes() {
|
static get propTypes() {
|
||||||
|
|||||||
35
app/screens/Modal.js
Normal file
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 { connect } from 'react-redux';
|
||||||
|
|
||||||
import { fetchProfile, updateProfile } from '../actions/profile.js';
|
import { fetchProfile, updateProfile } from '../actions/profile.js';
|
||||||
import { getNomDeBid, getProfile, isAllowedToBid } from '../selectors/profile.js';
|
import { getProfile, isAllowedToBid } from '../selectors/profile.js';
|
||||||
|
|
||||||
import Profile from './Profile.js';
|
import Profile from './Profile.js';
|
||||||
|
|
||||||
@@ -13,13 +13,8 @@ const matchStateToProps = (state) => {
|
|||||||
hasLinkedFacebook: profile.get('hasLinkedFacebook'),
|
hasLinkedFacebook: profile.get('hasLinkedFacebook'),
|
||||||
hasLinkedGoogle: profile.get('hasLinkedGoogle'),
|
hasLinkedGoogle: profile.get('hasLinkedGoogle'),
|
||||||
hasLocalAccount: profile.get('hasLocalAccount'),
|
hasLocalAccount: profile.get('hasLocalAccount'),
|
||||||
hasRegisteredAcccount: profile.get('hasRegisteredAcccount'),
|
|
||||||
id: profile.get('id'),
|
|
||||||
isAllowedToBid: isAllowedToBid(state),
|
isAllowedToBid: isAllowedToBid(state),
|
||||||
isVerified: profile.get('isVerified'),
|
isVerified: profile.get('isVerified'),
|
||||||
lastName: profile.get('lastName'),
|
|
||||||
nomDeBid: getNomDeBid(state),
|
|
||||||
organizationIdentifier: profile.get('organizationIdentifier'),
|
|
||||||
paymentToken: profile.get('paymentToken'),
|
paymentToken: profile.get('paymentToken'),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,26 +1,95 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { StyleSheet, Text, View } from 'react-native';
|
import PropTypes from 'prop-types';
|
||||||
|
import { Text, View } from 'react-native';
|
||||||
|
|
||||||
|
import ProfileUtility from '../components/Profile/Profile.container.js';
|
||||||
|
|
||||||
|
import styles from './Profile.styles.js';
|
||||||
|
|
||||||
|
const STRINGS = {
|
||||||
|
EMAIL_NEEDS_VERIFICATION: 'Your acount has not been verified, please check your email.',
|
||||||
|
};
|
||||||
|
|
||||||
export default class Profile extends Component {
|
export default class Profile extends Component {
|
||||||
|
static get propTypes() {
|
||||||
|
return {
|
||||||
|
hasLinkedApple: PropTypes.bool,
|
||||||
|
hasLinkedFacebook: PropTypes.bool,
|
||||||
|
hasLinkedGoogle: PropTypes.bool,
|
||||||
|
hasLocalAccount: PropTypes.bool,
|
||||||
|
isAllowedToBid: PropTypes.bool,
|
||||||
|
isVerified: PropTypes.bool,
|
||||||
|
paymentToken: PropTypes.string,
|
||||||
|
updateProfile: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static get defaultProps() {
|
||||||
|
return {
|
||||||
|
hasLinkedApple: false,
|
||||||
|
hasLinkedFacebook: false,
|
||||||
|
hasLinkedGoogle: false,
|
||||||
|
hasLocalAccount: false,
|
||||||
|
isAllowedToBid: false,
|
||||||
|
isVerified: false,
|
||||||
|
paymentToken: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const {
|
||||||
|
hasLinkedApple,
|
||||||
|
hasLinkedFacebook,
|
||||||
|
hasLinkedGoogle,
|
||||||
|
hasLocalAccount,
|
||||||
|
isAllowedToBid,
|
||||||
|
isVerified,
|
||||||
|
paymentToken,
|
||||||
|
updateProfile,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Text style={styles.title}>Profile</Text>
|
{!isVerified && (
|
||||||
|
<View style={styles.alertBar}>
|
||||||
|
<Text style={styles.alert}>{STRINGS.EMAIL_NEEDS_VERIFICATION}</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<ProfileUtility
|
||||||
|
cancelEditAction={() => false}
|
||||||
|
saveProfileAction={updateProfile}
|
||||||
|
saveProfileLabel="Update profile"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{!isAllowedToBid ? (
|
||||||
|
/* ADD PAYMENT METHOD */
|
||||||
|
<View />
|
||||||
|
) : (
|
||||||
|
/* SHOW/EDIT PAYMENT METHOD */
|
||||||
|
<View />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!hasLocalAccount && (
|
||||||
|
/* CREATE LOCAL ACCOUNT PASSWORD CTA */
|
||||||
|
<View />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{hasLinkedApple && (
|
||||||
|
/* APPLE LINK/UNLINK */
|
||||||
|
<View />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{hasLinkedFacebook && (
|
||||||
|
/* FACEBOOK LINK/UNLINK */
|
||||||
|
<View />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{hasLinkedGoogle && (
|
||||||
|
/* GOOGLE LINK/UNLINK */
|
||||||
|
<View />
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
container: {
|
|
||||||
flex: 1,
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
backgroundColor: '#F5FCFF',
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
fontSize: 20,
|
|
||||||
textAlign: 'center',
|
|
||||||
margin: 10,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|||||||
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 { Text, View } from 'react-native';
|
||||||
|
|
||||||
|
import CreateProfile from '../components/Profile/CreateProfile.js';
|
||||||
|
|
||||||
import styles from './Register.styles.js';
|
import styles from './Register.styles.js';
|
||||||
|
|
||||||
const STRINGS = {
|
export default function Register({ doRegistration, navigation }) {
|
||||||
NOM_EXPLANATION: 'Selecting a nom de bid allows you to bid anonymously - or not. By default, we\'ll use your first initial and last name.',
|
const title = 'Register';
|
||||||
SUBMIT_REGISTRATION: 'Register',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class Register extends Component {
|
const _doRegistration = (profile) => {
|
||||||
static get propTypes() {
|
if (!profile) {
|
||||||
return {
|
|
||||||
checkEmail: PropTypes.func.isRequired,
|
|
||||||
checkNomDeBid: PropTypes.func.isRequired,
|
|
||||||
doRegistration: PropTypes.func.isRequired,
|
|
||||||
// invalidEmail: PropTypes.bool.isRequired,
|
|
||||||
// invalidNomDeBid: PropTypes.bool.isRequired,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
addresses: [],
|
|
||||||
avatar: null,
|
|
||||||
email: null,
|
|
||||||
firstName: null,
|
|
||||||
lastName: null,
|
|
||||||
nomDeBid: null,
|
|
||||||
invalidEmail: false,
|
|
||||||
invalidNomDeBid: false,
|
|
||||||
password: null,
|
|
||||||
phones: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
this._doRegistration = this._doRegistration.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
_doRegistration() {
|
|
||||||
if (!this.isFormComplete()) {
|
|
||||||
console.error('Incomplete form... how did the button become enabled?');
|
|
||||||
alert('Please complete all of the required fields. They have bold labels.');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.doRegistration(this.getUserRegistration());
|
doRegistration(profile);
|
||||||
}
|
};
|
||||||
|
|
||||||
_validateEmail() {
|
|
||||||
this.props.checkEmail(this.state.email, (valid) => this.setState('invalidEmail', valid));
|
|
||||||
}
|
|
||||||
|
|
||||||
_validateNomDeBid() {
|
|
||||||
this.props.checkNomDeBid(this.state.nomDeBid, (valid) =>
|
|
||||||
this.setState('invalidNomDeBid', valid),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getUserRegistration() {
|
|
||||||
return {
|
|
||||||
addresses: this.state.addresses,
|
|
||||||
avatar: this.state.avatar,
|
|
||||||
email: this.state.email,
|
|
||||||
firstName: this.state.firstName,
|
|
||||||
lastName: this.state.lastName,
|
|
||||||
nomDeBid: this.state.nomDeBid,
|
|
||||||
password: this.state.password,
|
|
||||||
phones: this.state.phones,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
isFormComplete() {
|
|
||||||
return (
|
|
||||||
!this.state.invalidEmail &&
|
|
||||||
!this.state.invalidNomDeBid &&
|
|
||||||
!!this.state.firstName &&
|
|
||||||
!!this.state.lastName &&
|
|
||||||
!!this.state.email &&
|
|
||||||
!!this.state.nomDeBid &&
|
|
||||||
!!this.state.phones.length &&
|
|
||||||
!!this.state.password
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Text style={styles.title}>Register</Text>
|
<Text style={styles.heading}>{title}</Text>
|
||||||
<View style={styles.nameWrap}>
|
<CreateProfile
|
||||||
<TextInput
|
cancelEditAction={() => navigation.goBack()}
|
||||||
onChange={(text) => this.setState({ firstName: text })}
|
saveProfileAction={_doRegistration}
|
||||||
placeholder="first name"
|
showPasswordEntry
|
||||||
style={styles.textInput}
|
/>
|
||||||
value={this.state.firstName}
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
onChange={(text) => this.setState({ lastName: text })}
|
|
||||||
placeholder="last name"
|
|
||||||
style={styles.textInput}
|
|
||||||
value={this.state.lastName}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.emailWrap}>
|
|
||||||
<TextInput
|
|
||||||
keyboardType="email-address"
|
|
||||||
onChangeText={(text) => _updateState('username', text)}
|
|
||||||
onEndEditing={(text) => this._validateEmail(text)}
|
|
||||||
placeholder="email address"
|
|
||||||
style={styles.textInput}
|
|
||||||
value={this.state.email}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<View style={styles.nomWrap}>
|
|
||||||
<Text style={styles.hintText}>{STRINGS.NOM_EXPLANATION}</Text>
|
|
||||||
<TextInput
|
|
||||||
keyboardType="email-address"
|
|
||||||
onChangeText={(text) => _updateState('username', text)}
|
|
||||||
onEndEditing={(text) => this._validateEmail(text)}
|
|
||||||
placeholder="email address"
|
|
||||||
style={styles.textInput}
|
|
||||||
value={this.state.email}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<View style={styles.register}>
|
|
||||||
<Button title={STRINGS.SUBMIT_REGISTRATION} onPress={this._doRegistration} />
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Register.propTypes = {
|
||||||
|
doRegistration: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
export default (styles = StyleSheet.create({
|
export default StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
},
|
},
|
||||||
title: {},
|
heading: {},
|
||||||
localLogin: {},
|
});
|
||||||
services: {},
|
|
||||||
register: {},
|
|
||||||
}));
|
|
||||||
|
|||||||
@@ -1,37 +1,26 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { Text, View } from 'react-native';
|
import { Button, Text, View } from 'react-native';
|
||||||
import { Button } from 'react-native-elements';
|
|
||||||
|
|
||||||
import FacebookLogin from '../components/Login/FacebookLogin.container.js';
|
import FacebookLogin from '../components/Login/FacebookLogin.container.js';
|
||||||
import LocalLogin from '../components/Login/LocalLogin.container.js';
|
import LocalLogin from '../components/Login/LocalLogin.container.js';
|
||||||
|
|
||||||
import styles from './SignInOrRegister.styles.js';
|
import styles from './SignInOrRegister.styles.js';
|
||||||
|
|
||||||
export default class SignInOrRegister extends Component {
|
export default function SignInOrRegister({ navigation }) {
|
||||||
constructor() {
|
const title = 'Sign In or Register';
|
||||||
super(props);
|
|
||||||
|
|
||||||
this._doRegistration = this._doRegistration.bind(this);
|
return (
|
||||||
}
|
<View style={styles.container}>
|
||||||
|
<Text style={styles.title}>{title}</Text>
|
||||||
_doRegistration() {
|
<View style={styles.localLogin}>
|
||||||
this.props.navigation.navigate('Register');
|
<LocalLogin />
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<View style={styles.container}>
|
|
||||||
<Text style={styles.title}>Sign In or Register</Text>
|
|
||||||
<View style={styles.localLogin}>
|
|
||||||
<LocalLogin />
|
|
||||||
</View>
|
|
||||||
<View style={styles.services}>
|
|
||||||
<FacebookLogin />
|
|
||||||
</View>
|
|
||||||
<View style={styles.register}>
|
|
||||||
<Button title="Signup with Email" onPress={this._doRegistration} />
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
);
|
<View style={styles.services}>
|
||||||
}
|
<FacebookLogin />
|
||||||
|
</View>
|
||||||
|
<View style={styles.register}>
|
||||||
|
<Button title="Signup with Email" onPress={() => navigation.navigate('Register')} />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
export default (styles = StyleSheet.create({
|
export default StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
@@ -12,4 +12,4 @@ export default (styles = StyleSheet.create({
|
|||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
margin: 10,
|
margin: 10,
|
||||||
},
|
},
|
||||||
}));
|
});
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ import { StyleSheet, Text, View } from 'react-native';
|
|||||||
|
|
||||||
export default class Ticketing extends Component {
|
export default class Ticketing extends Component {
|
||||||
render() {
|
render() {
|
||||||
|
const title = 'Ticketing';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Text style={styles.title}>Ticketing</Text>
|
<Text style={styles.title}>{title}</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { Map } from 'immutable';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
const getState = (state) => state;
|
const getState = (state) => state;
|
||||||
@@ -16,7 +17,7 @@ export const getAuctionStatus = (state, itemId) => state.getIn(['actions', itemI
|
|||||||
|
|
||||||
export const getAuctionStatuses = createSelector(
|
export const getAuctionStatuses = createSelector(
|
||||||
[getState],
|
[getState],
|
||||||
(state) => state.get('actions') || new Map(),
|
(state) => state.get('autions') || new Map(),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getItemsIdsWithNoBids = createSelector(
|
export const getItemsIdsWithNoBids = createSelector(
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ export const getActiveEvent = createSelector(
|
|||||||
);
|
);
|
||||||
|
|
||||||
export const getDefaultEvent = createSelector(
|
export const getDefaultEvent = createSelector(
|
||||||
[getEvents],
|
[getActiveEvent, getEvents],
|
||||||
(eventsAsMap) => eventsAsMap.first(),
|
(activeEvent, eventsAsMap) => activeEvent || eventsAsMap.first(),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getEventsAsList = createSelector(
|
export const getEventsAsList = createSelector(
|
||||||
|
|||||||
@@ -18,7 +18,27 @@ export const getProfileAvatarUrl = createSelector(
|
|||||||
(profile) => profile.get('avatar'),
|
(profile) => profile.get('avatar'),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const getUserId = createSelector(
|
||||||
|
[getProfile],
|
||||||
|
(profile) => profile.get('id'),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getUserInitials = createSelector(
|
||||||
|
[getProfile],
|
||||||
|
(profile) => profile.get('initials'),
|
||||||
|
);
|
||||||
|
|
||||||
export const isAllowedToBid = createSelector(
|
export const isAllowedToBid = createSelector(
|
||||||
[getProfile],
|
[getProfile],
|
||||||
(profile) => profile.get('isAllowedToBid'),
|
(profile) => profile.get('isAllowedToBid'),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const isGeneratedNomDeBid = createSelector(
|
||||||
|
[getProfile],
|
||||||
|
(profile) => profile.get('generatedNomDeBid'),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const isRegisteredAccount = createSelector(
|
||||||
|
[getProfile],
|
||||||
|
(profile) => profile.get('isRegisteredAccount'),
|
||||||
|
);
|
||||||
|
|||||||
@@ -848,9 +848,43 @@
|
|||||||
);
|
);
|
||||||
inputPaths = (
|
inputPaths = (
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Eventment/Pods-Eventment-resources.sh",
|
"${PODS_ROOT}/Target Support Files/Pods-Eventment/Pods-Eventment-resources.sh",
|
||||||
|
"${PODS_ROOT}/FBSDKCoreKit/FacebookSDKStrings.bundle",
|
||||||
|
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf",
|
||||||
|
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf",
|
||||||
|
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf",
|
||||||
|
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Feather.ttf",
|
||||||
|
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf",
|
||||||
|
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Brands.ttf",
|
||||||
|
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Regular.ttf",
|
||||||
|
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Solid.ttf",
|
||||||
|
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Fontisto.ttf",
|
||||||
|
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Foundation.ttf",
|
||||||
|
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf",
|
||||||
|
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf",
|
||||||
|
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf",
|
||||||
|
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Octicons.ttf",
|
||||||
|
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf",
|
||||||
|
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf",
|
||||||
);
|
);
|
||||||
name = "[CP] Copy Pods Resources";
|
name = "[CP] Copy Pods Resources";
|
||||||
outputPaths = (
|
outputPaths = (
|
||||||
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FacebookSDKStrings.bundle",
|
||||||
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf",
|
||||||
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf",
|
||||||
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EvilIcons.ttf",
|
||||||
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Feather.ttf",
|
||||||
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome.ttf",
|
||||||
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Brands.ttf",
|
||||||
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Regular.ttf",
|
||||||
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Solid.ttf",
|
||||||
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Fontisto.ttf",
|
||||||
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Foundation.ttf",
|
||||||
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Ionicons.ttf",
|
||||||
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialCommunityIcons.ttf",
|
||||||
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialIcons.ttf",
|
||||||
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Octicons.ttf",
|
||||||
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SimpleLineIcons.ttf",
|
||||||
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf",
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
|
|||||||
@@ -1,6 +1,16 @@
|
|||||||
PODS:
|
PODS:
|
||||||
- boost-for-react-native (1.63.0)
|
- boost-for-react-native (1.63.0)
|
||||||
- DoubleConversion (1.1.6)
|
- DoubleConversion (1.1.6)
|
||||||
|
- FBSDKCoreKit (5.3.0):
|
||||||
|
- FBSDKCoreKit/Basics (= 5.3.0)
|
||||||
|
- FBSDKCoreKit/Core (= 5.3.0)
|
||||||
|
- FBSDKCoreKit/Basics (5.3.0)
|
||||||
|
- FBSDKCoreKit/Core (5.3.0):
|
||||||
|
- FBSDKCoreKit/Basics
|
||||||
|
- FBSDKLoginKit (5.3.0):
|
||||||
|
- FBSDKCoreKit (~> 5.0)
|
||||||
|
- FBSDKShareKit (5.3.0):
|
||||||
|
- FBSDKCoreKit (~> 5.0)
|
||||||
- Folly (2018.10.22.00):
|
- Folly (2018.10.22.00):
|
||||||
- boost-for-react-native
|
- boost-for-react-native
|
||||||
- DoubleConversion
|
- DoubleConversion
|
||||||
@@ -57,6 +67,20 @@ PODS:
|
|||||||
- React-cxxreact (= 0.60.0)
|
- React-cxxreact (= 0.60.0)
|
||||||
- React-jsi (= 0.60.0)
|
- React-jsi (= 0.60.0)
|
||||||
- React-jsinspector (0.60.0)
|
- React-jsinspector (0.60.0)
|
||||||
|
- react-native-fbsdk (1.0.1):
|
||||||
|
- React
|
||||||
|
- react-native-fbsdk/Core (= 1.0.1)
|
||||||
|
- react-native-fbsdk/Login (= 1.0.1)
|
||||||
|
- react-native-fbsdk/Share (= 1.0.1)
|
||||||
|
- react-native-fbsdk/Core (1.0.1):
|
||||||
|
- FBSDKCoreKit (~> 5.0)
|
||||||
|
- React
|
||||||
|
- react-native-fbsdk/Login (1.0.1):
|
||||||
|
- FBSDKLoginKit (~> 5.0)
|
||||||
|
- React
|
||||||
|
- react-native-fbsdk/Share (1.0.1):
|
||||||
|
- FBSDKShareKit (~> 5.0)
|
||||||
|
- React
|
||||||
- React-RCTActionSheet (0.60.0):
|
- React-RCTActionSheet (0.60.0):
|
||||||
- React-Core (= 0.60.0)
|
- React-Core (= 0.60.0)
|
||||||
- React-RCTAnimation (0.60.0):
|
- React-RCTAnimation (0.60.0):
|
||||||
@@ -81,10 +105,14 @@ PODS:
|
|||||||
- React-RCTWebSocket (0.60.0):
|
- React-RCTWebSocket (0.60.0):
|
||||||
- React-Core (= 0.60.0)
|
- React-Core (= 0.60.0)
|
||||||
- React-fishhook (= 0.60.0)
|
- React-fishhook (= 0.60.0)
|
||||||
|
- RNCAsyncStorage (1.6.1):
|
||||||
|
- React
|
||||||
- RNGestureHandler (1.3.0):
|
- RNGestureHandler (1.3.0):
|
||||||
- React
|
- React
|
||||||
- RNScreens (1.0.0-alpha.23):
|
- RNScreens (1.0.0-alpha.23):
|
||||||
- React
|
- React
|
||||||
|
- RNSecureStorage (0.1.2):
|
||||||
|
- React
|
||||||
- RNVectorIcons (6.6.0):
|
- RNVectorIcons (6.6.0):
|
||||||
- React
|
- React
|
||||||
- yoga (0.60.0.React)
|
- yoga (0.60.0.React)
|
||||||
@@ -101,6 +129,7 @@ DEPENDENCIES:
|
|||||||
- React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
|
- React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
|
||||||
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
|
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
|
||||||
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
|
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
|
||||||
|
- react-native-fbsdk (from `/Users/mfitzpatrick/Documents/Personal/eventment-app/node_modules/react-native-fbsdk`)
|
||||||
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
|
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
|
||||||
- React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`)
|
- React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`)
|
||||||
- React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`)
|
- React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`)
|
||||||
@@ -111,14 +140,19 @@ DEPENDENCIES:
|
|||||||
- React-RCTText (from `../node_modules/react-native/Libraries/Text`)
|
- React-RCTText (from `../node_modules/react-native/Libraries/Text`)
|
||||||
- React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
|
- React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
|
||||||
- React-RCTWebSocket (from `../node_modules/react-native/Libraries/WebSocket`)
|
- React-RCTWebSocket (from `../node_modules/react-native/Libraries/WebSocket`)
|
||||||
- RNGestureHandler (from `/Users/mifi/Temporary Projects/eventment-app/node_modules/react-native-gesture-handler`)
|
- "RNCAsyncStorage (from `/Users/mfitzpatrick/Documents/Personal/eventment-app/node_modules/@react-native-community/async-storage`)"
|
||||||
- RNScreens (from `/Users/mifi/Temporary Projects/eventment-app/node_modules/react-native-screens`)
|
- RNGestureHandler (from `/Users/mfitzpatrick/Documents/Personal/eventment-app/node_modules/react-native-gesture-handler`)
|
||||||
- RNVectorIcons (from `/Users/mifi/Temporary Projects/eventment-app/node_modules/react-native-vector-icons`)
|
- RNScreens (from `/Users/mfitzpatrick/Documents/Personal/eventment-app/node_modules/react-native-screens`)
|
||||||
|
- RNSecureStorage (from `/Users/mfitzpatrick/Documents/Personal/eventment-app/node_modules/react-native-secure-storage`)
|
||||||
|
- RNVectorIcons (from `../node_modules/react-native-vector-icons`)
|
||||||
- yoga (from `../node_modules/react-native/ReactCommon/yoga`)
|
- yoga (from `../node_modules/react-native/ReactCommon/yoga`)
|
||||||
|
|
||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
https://github.com/cocoapods/specs.git:
|
https://github.com/cocoapods/specs.git:
|
||||||
- boost-for-react-native
|
- boost-for-react-native
|
||||||
|
- FBSDKCoreKit
|
||||||
|
- FBSDKLoginKit
|
||||||
|
- FBSDKShareKit
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
DoubleConversion:
|
DoubleConversion:
|
||||||
@@ -143,6 +177,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../node_modules/react-native/ReactCommon/jsiexecutor"
|
:path: "../node_modules/react-native/ReactCommon/jsiexecutor"
|
||||||
React-jsinspector:
|
React-jsinspector:
|
||||||
:path: "../node_modules/react-native/ReactCommon/jsinspector"
|
:path: "../node_modules/react-native/ReactCommon/jsinspector"
|
||||||
|
react-native-fbsdk:
|
||||||
|
:path: "/Users/mfitzpatrick/Documents/Personal/eventment-app/node_modules/react-native-fbsdk"
|
||||||
React-RCTActionSheet:
|
React-RCTActionSheet:
|
||||||
:path: "../node_modules/react-native/Libraries/ActionSheetIOS"
|
:path: "../node_modules/react-native/Libraries/ActionSheetIOS"
|
||||||
React-RCTAnimation:
|
React-RCTAnimation:
|
||||||
@@ -163,18 +199,25 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../node_modules/react-native/Libraries/Vibration"
|
:path: "../node_modules/react-native/Libraries/Vibration"
|
||||||
React-RCTWebSocket:
|
React-RCTWebSocket:
|
||||||
:path: "../node_modules/react-native/Libraries/WebSocket"
|
:path: "../node_modules/react-native/Libraries/WebSocket"
|
||||||
|
RNCAsyncStorage:
|
||||||
|
:path: "/Users/mfitzpatrick/Documents/Personal/eventment-app/node_modules/@react-native-community/async-storage"
|
||||||
RNGestureHandler:
|
RNGestureHandler:
|
||||||
:path: "/Users/mifi/Temporary Projects/eventment-app/node_modules/react-native-gesture-handler"
|
:path: "/Users/mfitzpatrick/Documents/Personal/eventment-app/node_modules/react-native-gesture-handler"
|
||||||
RNScreens:
|
RNScreens:
|
||||||
:path: "/Users/mifi/Temporary Projects/eventment-app/node_modules/react-native-screens"
|
:path: "/Users/mfitzpatrick/Documents/Personal/eventment-app/node_modules/react-native-screens"
|
||||||
|
RNSecureStorage:
|
||||||
|
:path: "/Users/mfitzpatrick/Documents/Personal/eventment-app/node_modules/react-native-secure-storage"
|
||||||
RNVectorIcons:
|
RNVectorIcons:
|
||||||
:path: "/Users/mifi/Temporary Projects/eventment-app/node_modules/react-native-vector-icons"
|
:path: "../node_modules/react-native-vector-icons"
|
||||||
yoga:
|
yoga:
|
||||||
:path: "../node_modules/react-native/ReactCommon/yoga"
|
:path: "../node_modules/react-native/ReactCommon/yoga"
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
|
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
|
||||||
DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2
|
DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2
|
||||||
|
FBSDKCoreKit: 39748deefe37a005f983722e433be2ddcdd83ae7
|
||||||
|
FBSDKLoginKit: bc0329e10045789e8d40b12d4f15ddbd07a898e2
|
||||||
|
FBSDKShareKit: f61d03f36ce4f1b379c7de7dbfe173662e68be44
|
||||||
Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51
|
Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51
|
||||||
glog: 1f3da668190260b06b429bb211bfbee5cd790c28
|
glog: 1f3da668190260b06b429bb211bfbee5cd790c28
|
||||||
React: 4b3c068e793e96672dcd186a2b572fac43e4b031
|
React: 4b3c068e793e96672dcd186a2b572fac43e4b031
|
||||||
@@ -185,6 +228,7 @@ SPEC CHECKSUMS:
|
|||||||
React-jsi: 8e128c4d0d8febc2977ef617d1c09bb54326069c
|
React-jsi: 8e128c4d0d8febc2977ef617d1c09bb54326069c
|
||||||
React-jsiexecutor: 7a3554f703a58963ec80b860144ea0f0e9b910e1
|
React-jsiexecutor: 7a3554f703a58963ec80b860144ea0f0e9b910e1
|
||||||
React-jsinspector: d4ed52225912efe0019bb7f1a225aec20f23049a
|
React-jsinspector: d4ed52225912efe0019bb7f1a225aec20f23049a
|
||||||
|
react-native-fbsdk: 080f3bb23513c5b59b6528543206fe0d1436bb7e
|
||||||
React-RCTActionSheet: b27ff3cf3a68f917c46d2b94abf938b625b96570
|
React-RCTActionSheet: b27ff3cf3a68f917c46d2b94abf938b625b96570
|
||||||
React-RCTAnimation: 9e4708e5bd65fca8285ce7c0aa076f3f4fa5c2f8
|
React-RCTAnimation: 9e4708e5bd65fca8285ce7c0aa076f3f4fa5c2f8
|
||||||
React-RCTBlob: 6eafcc3a24f33785692a7be24918ade607bc8719
|
React-RCTBlob: 6eafcc3a24f33785692a7be24918ade607bc8719
|
||||||
@@ -195,11 +239,13 @@ SPEC CHECKSUMS:
|
|||||||
React-RCTText: 685fca2e13b024271048e7e247ef24476f28a41e
|
React-RCTText: 685fca2e13b024271048e7e247ef24476f28a41e
|
||||||
React-RCTVibration: 4ee1cf208ab17a50fafb1c16ffe28fe594a64e4f
|
React-RCTVibration: 4ee1cf208ab17a50fafb1c16ffe28fe594a64e4f
|
||||||
React-RCTWebSocket: fca087d583724aa0e5fef7d911f0f2a28d0f2736
|
React-RCTWebSocket: fca087d583724aa0e5fef7d911f0f2a28d0f2736
|
||||||
|
RNCAsyncStorage: 2e2e3feb9bdadc752a026703d8c4065ca912e75a
|
||||||
RNGestureHandler: 5329a942fce3d41c68b84c2c2276ce06a696d8b0
|
RNGestureHandler: 5329a942fce3d41c68b84c2c2276ce06a696d8b0
|
||||||
RNScreens: f28b48b8345f2f5f39ed6195518291515032a788
|
RNScreens: f28b48b8345f2f5f39ed6195518291515032a788
|
||||||
|
RNSecureStorage: 24d433f7673a0daade43689de805933a70641731
|
||||||
RNVectorIcons: 0bb4def82230be1333ddaeee9fcba45f0b288ed4
|
RNVectorIcons: 0bb4def82230be1333ddaeee9fcba45f0b288ed4
|
||||||
yoga: 616fde658be980aa60a2158835170f3f9c2d04b4
|
yoga: 616fde658be980aa60a2158835170f3f9c2d04b4
|
||||||
|
|
||||||
PODFILE CHECKSUM: 26223b0d86281b1c70ae009fe120443ad6ad9bf9
|
PODFILE CHECKSUM: 9a233bb7438f412e17eeb945a7c5f563882773de
|
||||||
|
|
||||||
COCOAPODS: 1.7.4
|
COCOAPODS: 1.6.1
|
||||||
|
|||||||
Reference in New Issue
Block a user