diff --git a/app/App.js b/app/App.js index 5f821e9..c0b07db 100644 --- a/app/App.js +++ b/app/App.js @@ -6,7 +6,7 @@ * @flow */ -import React, {Fragment} from 'react'; +import React from 'react'; import { createAppContainer } from 'react-navigation'; import { Tabs } from './router.js'; diff --git a/app/actions/events.js b/app/actions/events.js index a926f1b..b52ef30 100644 --- a/app/actions/events.js +++ b/app/actions/events.js @@ -1,33 +1,36 @@ import { List } from 'immutable'; -import { getEndpointUrl } from '../api/index.js'; - +import { blockUI, unblockUI } from './index.js'; +import { fetchEvents as fetchEventsApi } from '../api/events.js'; import { + EVENTS_LOAD_FAILED, EVENTS_LOADED, GET_EVENTS, } from '../constants/actionTypes.js'; - -import { blockUI, unblockUI } from './index.js'; -import { API_ENDPOINTS } from '../constants/constants.js'; - import Event from '../domain/Event.js'; +import { getAuthToken } from '../selectors/auth.js'; +const eventsLoaded = (payload) => ({ type: EVENTS_LOADED, payload }); -const eventsLoadSuccess = (events, dispatch) => { - const payload = List(events).map((i) => Event.fromJS(i)); - dispatch({ type: EVENTS_LOADED, payload }); +const eventsLoadError = (payload) => ({ type: EVENTS_LOAD_FAILED, payload }); + +const eventsFetchSuccess = (items) => (dispatch) => { + const payload = List(items).map((i) => Event.fromJS(i)); + + dispatch(eventsLoaded(payload)); dispatch(unblockUI); }; -export const setActiveEvent = (eventId) => ({ - type: SET_ACTIVE_EVENT, - payload: eventId, -}); - -export const fetchEvents = () => (dispatch) => { - dispatch(blockUI()); - fetch(getEndpointUr(API_ENDPOINTS.GET_EVENTS)) - .then(response => response.json()) - .then(payload => eventsLoadSuccess(payload, dispatch)) - .catch(err => console.error('[actions::getEvents]', err)); +const eventsFetchFailure = (error) => (dispatch) => { + console.error('[actions::events::eventsFetchFailure]', error); + dispatch(eventsLoadError(error)); + dispatch(unblockUI); +}; + +export const fetchEvents = () => (dispatch, getState) => { + const authToken = getAuthToken(getState()); + + fetchEventsApi(authToken) + .then(payload => dispatch(eventsFetchSuccess(payload))) + .catch(err => dispatch(eventsFetchFailure(err))); }; diff --git a/app/actions/items.js b/app/actions/items.js index 73a1c05..becac35 100644 --- a/app/actions/items.js +++ b/app/actions/items.js @@ -1,12 +1,12 @@ import { List } from 'immutable'; -import { fetchItems } from '../api/items.js'; +import { fetchItems as fetchItemsApi } from '../api/items.js'; import { GET_ITEMS, ITEMS_LOADED, } from '../constants/actionTypes.js'; import { getActiveEventId } from '../selectors/activeEvent.js'; -import { getLoginToken } from '../selectors/profile.js'; +import { getAuthToken } from '../selectors/auth.js'; import { blockUI, unblockUI } from './index.js'; @@ -24,16 +24,16 @@ const itemsFetchSuccess = (items) => (dispatch) => { }; const itemsFetchFailure = (error) => (dispatch) => { - console.error('[actions::getItems]', error)); + console.error('[actions::items::itemsFetchFailure]', error); dispatch(itemsLoadFailure(error)); dispatch(unblockUI); }; export const fetchItems = () => (dispatch, getState) => { const eventId = getActiveEventId(getState()); - const authToken = getLoginToken(getState()); + const authToken = getAuthToken(getState()); - fetchItems(activeEvent, authToken) + fetchItemsApi(activeEvent, authToken) .then(payload => dispatch(itemsFetchSuccess(payload))) - .catch(err => dispatch(itemsFetchFailure(err)); + .catch(err => dispatch(itemsFetchFailure(err))); }; diff --git a/app/actions/profile.js b/app/actions/profile.js index cd29173..9e6b0da 100644 --- a/app/actions/profile.js +++ b/app/actions/profile.js @@ -3,13 +3,18 @@ import { blockUI, unblockUI } from './index.js'; import { getEmailAvailability, getNomAvailaibility, + loginUser, registerNewUser, } from '../api/profile.js'; import { DO_LOGOUT, + LOGIN_FAILURE, + LOGIN_SUCCESS, PROFILE_EMAIL_AVAILABLE, PROFILE_NOM_AVAILABLE, + UNSET_AUTH, + UNSET_PROFILE, UPDATE_PROFILE, } from '../constants/actionTypes.js'; @@ -23,6 +28,16 @@ const isValidNom = (payload) => ({ payload, }); +const loginFailure = (payload) => ({ + type: LOGIN_FAILURE, + payload, +}); + +const loginSuccess = (payload) => ({ + type: LOGIN_SUCCESS, + payload, +}); + const logoutUser = () => ({ type: DO_LOGOUT, }); @@ -37,6 +52,14 @@ const registrationSuccess = (payload) => ({ payload, }); +const unsetAuth = () => ({ + type: UNSET_AUTH, +}); + +const unsetProfile = () => ({ + type: UNSET_PROFILE, +}); + const updateProfile = (profile) => ({ type: UPDATE_PROFILE, payload: profile, @@ -50,8 +73,20 @@ export const checkNomAvailability = (nomDeBid) => (dispatch) => { }; +export const login = (username, password) => (dispatch) => { + dispatch(blockUI()); + loginUser(username, password) + .then(result => { + dispatch(loginSuccess(result)) + }) + .catch(err => dispatch(loginFailure(err))); +}; -export const logout = () => (dispatch) => dispatch(logoutUser()); +export const logout = () => (dispatch) => { + dispatch(unsetProfile()); + dispatch(unsetAuth()); + dispatch(logoutUser()); +}; // USER REGISTRATION const handleRegistrationSuccess = (user) => (dispatch) => { diff --git a/app/api/events.js b/app/api/events.js new file mode 100644 index 0000000..1cf72ae --- /dev/null +++ b/app/api/events.js @@ -0,0 +1,7 @@ +import { API_ENDPOINTS, requestGet } from './index.js'; + +export const fetchEvents = (auth) => { + const path = String(API_ENDPOINTS.GET_EVENTS); + const opts = { Authorization: auth ? `Bearer ${auth}` : null }; + return requestGet(path, null, opts); +}; diff --git a/app/api/helpers.js b/app/api/helpers.js index fbc2f47..2ddea3c 100644 --- a/app/api/helpers.js +++ b/app/api/helpers.js @@ -40,7 +40,7 @@ const parseQueryParamsString = (queryParams) => { }; const parseQueryParamsObject = (queryParams) => { - if (typeof queryParams !== 'object' && Array.isArray(queryParams)) { + if (queryParams === null || typeof queryParams !== 'object' || Array.isArray(queryParams)) { return null; } diff --git a/app/api/index.js b/app/api/index.js index f2cac76..408053b 100644 --- a/app/api/index.js +++ b/app/api/index.js @@ -11,7 +11,7 @@ import { API_URL } from '../constants/constants.js'; const DefaultRequestOptions = {}; -const endpoints = { +export const API_ENDPOINTS = { // Events and Items GET_EVENTS: '/events', // GET_ITEMS: '/items?eventId=:event_id', @@ -23,6 +23,9 @@ const endpoints = { PLACE_BID: '/bids/:item_id', PURCHASE_ITEM: '/sales', + // Login + LOGIN: '/auth', + // User/Profile USER_SIGNUP: '/signup', USER_PROFILE: '/users/:user_id', @@ -42,11 +45,11 @@ const cacheBuster = () => { }; export const getEndpointUrl = (endpoint) => { - if (!endpoints[endpoint]) { + if (!API_ENDPOINTS[endpoint]) { throw new Error('Invalid API endpoint specified'); } - return `${API_URL}${endpoints[endpoint]}`; //`${cacheBuster()}`; + return `${API_URL}${API_ENDPOINTS[endpoint]}`; //`${cacheBuster()}`; }; export const requestGet = (path, queryParams = [], requestOptions = {}) => { diff --git a/app/api/items.js b/app/api/items.js index 79a1580..f1ece26 100644 --- a/app/api/items.js +++ b/app/api/items.js @@ -1,4 +1,4 @@ -import { getEndpointUrl, requestGet } from './index.js'; +import { API_ENDPOINTS, requestGet } from './index.js'; export const fetchItems = (eventId, auth) => { const path = String(API_ENDPOINTS.GET_ITEMS).replace(/:event_id$/, eventId); diff --git a/app/api/profile.js b/app/api/profile.js index e69de29..4c92f81 100644 --- a/app/api/profile.js +++ b/app/api/profile.js @@ -0,0 +1,18 @@ +import { API_ENDPOINTS, requestGet } from './index.js'; + +export const getEmailAvailability = (email) => { +}; + +export const getNomAvailaibility = (nomDeBid) => { +}; + +export const loginUser = (username, password) => { + const path = String(API_ENDPOINTS.LOGIN); + return requestPost({ + path: API_ENDPOINTS.LOGIN, + body: { username, password }, + }); +}; + +export const registerNewUser = (user) => { +}; diff --git a/app/components/AppHeader/AppHeader.js b/app/components/AppHeader/AppHeader.js index 122125a..3c58171 100644 --- a/app/components/AppHeader/AppHeader.js +++ b/app/components/AppHeader/AppHeader.js @@ -3,20 +3,22 @@ import PropTypes from 'prop-types'; import { Header } from 'react-native-elements'; -import HeaderTitle from './HeaderTitle.container.js'; +import HeaderTitle from './HeaderTitle/HeaderTitle.container.js'; import HeaderContentLeft from './HeaderContentLeft.container.js'; import HeaderContentRight from './HeaderContentRight.container.js'; import styles from './AppHeader.styles.js'; -export default function AppHeader({ navigation }) ( -
} - centerComponent={} - rightComponent={} - /> -) +export default function AppHeader({ navigation }) { + return ( +
} + centerComponent={} + rightComponent={} + /> + ); +} AppHeader.propTypes = { navigation: PropTypes.func.isRequired, diff --git a/app/components/AppHeader/HeaderContentLeft.container.js b/app/components/AppHeader/HeaderContentLeft.container.js index e3ffc6d..a9b0d62 100644 --- a/app/components/AppHeader/HeaderContentLeft.container.js +++ b/app/components/AppHeader/HeaderContentLeft.container.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; -import { hasMultipleEvents } from '../selectors/events.js'; +import { hasMultipleEvents } from '../../selectors/events.js'; import HeaderContentLeft from './HeaderContentLeft.js'; diff --git a/app/components/AppHeader/HeaderContentRight.container.js b/app/components/AppHeader/HeaderContentRight.container.js index 5080ead..b4ad083 100644 --- a/app/components/AppHeader/HeaderContentRight.container.js +++ b/app/components/AppHeader/HeaderContentRight.container.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; -import { getProfileAvatarUrl } from '../selectors/profile.js'; +import { getProfileAvatarUrl } from '../../selectors/profile.js'; import HeaderContentRight from './HeaderContentRight.js'; diff --git a/app/components/AppHeader/HeaderTitle/EventTitle/EventTitle.container.js b/app/components/AppHeader/HeaderTitle/EventTitle/EventTitle.container.js index e90feb6..77bfcd8 100644 --- a/app/components/AppHeader/HeaderTitle/EventTitle/EventTitle.container.js +++ b/app/components/AppHeader/HeaderTitle/EventTitle/EventTitle.container.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; -import { getActiveEvent, getDefaultEvent } from '../selectors/events.js'; +import { getActiveEvent, getDefaultEvent } from '../../../../selectors/events.js'; import EventTitle from './EventTitle.js'; diff --git a/app/components/AppHeader/HeaderTitle/EventTitle/EventTitle.styles.js b/app/components/AppHeader/HeaderTitle/EventTitle/EventTitle.styles.js index befc216..1140069 100644 --- a/app/components/AppHeader/HeaderTitle/EventTitle/EventTitle.styles.js +++ b/app/components/AppHeader/HeaderTitle/EventTitle/EventTitle.styles.js @@ -1,6 +1,6 @@ import { StyleSheet } from 'react-native'; -export default const styles = StyleSheet.create({ +export const styles = StyleSheet.create({ eventInfo: { flexDirection: 'row', }, @@ -12,4 +12,3 @@ export default const styles = StyleSheet.create({ flex: 1, }, }); - diff --git a/app/components/AppHeader/HeaderTitle/HeaderTitle.container.js b/app/components/AppHeader/HeaderTitle/HeaderTitle.container.js index a59812a..1c3584a 100644 --- a/app/components/AppHeader/HeaderTitle/HeaderTitle.container.js +++ b/app/components/AppHeader/HeaderTitle/HeaderTitle.container.js @@ -1,7 +1,7 @@ import { connect } from 'react-redux'; -import { hasActiveEvent } from '../selectors/activeEvent.js'; -import { hasMultipleEvents } from '../selectors/events.js'; +import { hasActiveEvent } from '../../../selectors/activeEvent.js'; +import { hasMultipleEvents } from '../../../selectors/events.js'; import HeaderTitle from './HeaderTitle.js'; diff --git a/app/components/AppHeader/HeaderTitle/HeaderTitle.js b/app/components/AppHeader/HeaderTitle/HeaderTitle.js index 6833937..4a583e0 100644 --- a/app/components/AppHeader/HeaderTitle/HeaderTitle.js +++ b/app/components/AppHeader/HeaderTitle/HeaderTitle.js @@ -9,7 +9,7 @@ import { import EventTitle from './EventTitle/EventTitle.container.js'; -import styles from './TitleBar.styles.js'; +import styles from './HeaderTitle.styles.js'; export default function HeaderTitle({ activeRoute, diff --git a/app/components/AppHeader/HeaderTitle/HeaderTitle.styles.js b/app/components/AppHeader/HeaderTitle/HeaderTitle.styles.js index d070666..e0207bc 100644 --- a/app/components/AppHeader/HeaderTitle/HeaderTitle.styles.js +++ b/app/components/AppHeader/HeaderTitle/HeaderTitle.styles.js @@ -1,6 +1,6 @@ import { StyleSheet } from 'react-native'; -export default const styles = StyleSheet.create({ +export const styles = StyleSheet.create({ filterBar: { backgroundColor: '#0F0', flexDirection: 'row', diff --git a/app/components/AppHeader/IconButtons/BackIcon.js b/app/components/AppHeader/IconButtons/BackIcon.js index 6f5642a..dfa08e8 100644 --- a/app/components/AppHeader/IconButtons/BackIcon.js +++ b/app/components/AppHeader/IconButtons/BackIcon.js @@ -4,11 +4,13 @@ import PropTypes from 'prop-types'; import { TouchableOpacity } from 'react-native'; import { Icon } from 'react-native-elements'; -export default function BackIcon({ action }) ( - - ; - -) +export default function BackIcon({ action }) { + return ( + + ; + + ); +} BackIcon.propTypes = { action: PropTypes.func.isRequired, diff --git a/app/components/AppHeader/UserProfileButton/UserProfileButton.container.js b/app/components/AppHeader/UserProfileButton/UserProfileButton.container.js index 304da74..b0cb7a2 100644 --- a/app/components/AppHeader/UserProfileButton/UserProfileButton.container.js +++ b/app/components/AppHeader/UserProfileButton/UserProfileButton.container.js @@ -1,11 +1,11 @@ import { connect } from 'react-redux'; -import { getProfileAvatarUrl } from '../selectors/profile.js'; +import { getProfileAvatarUrl } from '../../../selectors/profile.js'; -import HeaderContentRight from './HeaderContentRight.js'; +import UserProfileButton from './UserProfileButton.js'; const matchStateToProps = (state) => ({ avatarUrl: getProfileAvatarUrl(state), }); -export default connect(matchStateToProps, null)(HeaderContentRight); +export default connect(matchStateToProps, null)(UserProfileButton); diff --git a/app/components/AppHeader/UserProfileButton/UserProfileButton.js b/app/components/AppHeader/UserProfileButton/UserProfileButton.js index 6fccdb1..61307ed 100644 --- a/app/components/AppHeader/UserProfileButton/UserProfileButton.js +++ b/app/components/AppHeader/UserProfileButton/UserProfileButton.js @@ -20,16 +20,16 @@ export default function UserProfileButton({ avatarUrl, navigation }) { ) : ( - ; + )} ); } -HeaderContentRight.propTypes = { +UserProfileButton.propTypes = { avatarUrl: PropTypes.string, }; -HeaderContentRight.propTypes = { +UserProfileButton.propTypes = { avatarUrl: null, }; diff --git a/app/components/FacebookLogin/FacebookLogin.container.js b/app/components/Login/FacebookLogin.container.js similarity index 94% rename from app/components/FacebookLogin/FacebookLogin.container.js rename to app/components/Login/FacebookLogin.container.js index 4004851..bd46217 100644 --- a/app/components/FacebookLogin/FacebookLogin.container.js +++ b/app/components/Login/FacebookLogin.container.js @@ -5,7 +5,7 @@ import { logout, registrationServiceError, userCanceledRegistration, -} from '../actions/profile.js'; +} from '../../actions/profile.js'; import FacebookLogin from './FacebookLogin.js'; diff --git a/app/components/FacebookLogin/FacebookLogin.js b/app/components/Login/FacebookLogin.js similarity index 100% rename from app/components/FacebookLogin/FacebookLogin.js rename to app/components/Login/FacebookLogin.js diff --git a/app/components/Login/LocalLogin.container.js b/app/components/Login/LocalLogin.container.js new file mode 100644 index 0000000..cdeb6a7 --- /dev/null +++ b/app/components/Login/LocalLogin.container.js @@ -0,0 +1,11 @@ +import { connect } from 'react-redux'; + +import { login } from '../../actions/profile.js'; + +import LocalLogin from './LocalLogin.js'; + +const mapDispatchToProps = (dispatch) => ({ + doLoginAction: (username, password) => dispatch(login(username, password)), +}); + +export default connect(null, mapDispatchToProps)(LocalLogin); diff --git a/app/components/Login/LocalLogin.js b/app/components/Login/LocalLogin.js new file mode 100644 index 0000000..0f0d621 --- /dev/null +++ b/app/components/Login/LocalLogin.js @@ -0,0 +1,55 @@ +import React, { useState } from 'react'; +import PropTypes from 'prop-types'; + +import { Button, TextInput, View } from 'react-native'; + +export default function LocalLogin({ doLoginAction }) { + + const [ enabled, setEnableSubmit ] = useState(false); + const [ password, setPassword ] = useState(null); + const [ username, setUsername ] = useState(null); + + const _handleLoginSubmit = () => { + doLoginAction(username, password); + }; + + const _updateState = (field, value) => { + if (field === 'username') { + setUsername(value); + } + + if (field === 'password') { + setPassword(value); + } + + if (!!username && !!password) { + setEnableSubmit(true); + } + }; + + return ( + + _updateState('username', text)} + value={username} + /> + _updateState('password', text)} + value={password} + /> +