diff --git a/src/actions/alert.js b/src/actions/alert.js
new file mode 100644
index 0000000..02e78ff
--- /dev/null
+++ b/src/actions/alert.js
@@ -0,0 +1,18 @@
+import types from '../types/alert';
+
+export const enablePriceAlertAction = (data) => ({
+ type: types.ENABLE_PRICE_ALERT,
+ payload: data
+});
+export const disablePriceAlertAction = (data) => ({
+ type: types.DISABLE_PRICE_ALERT,
+ payload: data
+});
+export const deletePriceAlertAction = (data) => ({
+ type: types.DELETE_PRICE_ALERT,
+ payload: data
+});
+export const fetchPriceAlertAction = (data) => ({
+ type: types.FETCH_PRICE_ALERT,
+ payload: data
+});
\ No newline at end of file
diff --git a/src/components/PriceAlert/create.js b/src/components/PriceAlert/create.js
new file mode 100644
index 0000000..c87dda0
--- /dev/null
+++ b/src/components/PriceAlert/create.js
@@ -0,0 +1,207 @@
+import React, { Component } from 'react';
+import { ScrollView, StyleSheet, View, Text, TextInput, Alert } from 'react-native';
+import { connect } from 'react-redux';
+import RadioForm, {RadioButton, RadioButtonInput, RadioButtonLabel} from 'react-native-simple-radio-button';
+import { baseColor, lossColor, brandColor } from '../../config'
+import { Button, StyleProvider, } from 'native-base';
+import getTheme from '../../../native-base-theme/components';
+import _platform from '../../../native-base-theme/variables/platform';
+import { withDrawer } from '../../helpers/drawer';
+import { createPriceAlert } from '../../reducers/alert'
+
+const styles = StyleSheet.create({
+ scrollContainer: {
+ backgroundColor: baseColor
+ },
+ container: {
+ display: 'flex',
+ borderColor: '#fff',
+ justifyContent: 'space-between',
+ backgroundColor: baseColor,
+ marginBottom: 10,
+ flexDirection: 'row',
+ paddingLeft: 20
+ },
+ containerChild: {
+ marginBottom: 15,
+ flexGrow: .5,
+ flexBasis: 1,
+ display: 'flex',
+ justifyContent: 'space-around',
+ flexDirection: 'row'
+ },
+ textInput: {
+ height: 30,
+ fontSize: 14,
+ width: '50%',
+ borderColor: 'gray',
+ borderBottomWidth: 1,
+ color: '#fff'
+ },
+ horizontalLine: {
+ borderBottomColor: '#f5f5f5',
+ borderBottomWidth: 1,
+ marginTop: 15,
+ marginBottom: 15
+ }
+})
+
+const radioProps = [
+ {label: 'One Time', value: 0 },
+ {label: 'Continuous', value: 1 }
+]
+
+const priceLevel = [
+ {label: 'More than', value: 0 },
+ {label: 'Less than', value: 1 }
+]
+
+class CreatePriceAlert extends Component {
+ state = {
+ radioValue: 0,
+ frequency: 0,
+ price: 0,
+ type: 0,
+ }
+
+ submit = () => {
+ const {currencySymbol, currency, createPriceAlert } = this.props;
+ if(this.state.price <= 0) {
+ Alert.alert('Please enter a correct price value');
+ return
+ }
+
+ createPriceAlert({
+ fsym : currencySymbol,
+ tsym: currency,
+ price: parseFloat(this.state.price),
+ type: parseInt(this.state.type),
+ frequency: parseInt(this.state.frequency)
+ })
+ }
+
+ render() {
+ const { tokenDetails, edit, price } = this.props
+
+ return (
+
+
+
+ THRESHOLD
+
+
+
+ PRICE
+ this.setState({price})}
+ keyboardType='numeric'
+ placeholder={'Enter numeric value'}
+ placeholderTextColor='#444'
+ />
+
+
+
+ Current
+
+
+
+
+ WHEN THE PRICE IS
+
+
+ {this.setState({type: value})}}
+ />
+
+
+
+ FREQUENCY
+
+
+
+ {this.setState({frequency: value})}}
+ />
+
+
+
+
+
+
+
+
+ )
+ }
+}
+
+
+const mapStateToProps = (state, props) => ({
+ tokenDetails: state.account.tokenDetails,
+ portfolio: state.account.portfolio,
+ currency: state.account.preference.currency,
+ ...props.navigation.state.params,
+})
+
+const mapDispatchToProps = (dispatch) => ({
+ createPriceAlert: (data) => dispatch(createPriceAlert(data))
+})
+
+export default connect(mapStateToProps, mapDispatchToProps)(withDrawer(CreatePriceAlert));
\ No newline at end of file
diff --git a/src/components/PriceAlert/index.js b/src/components/PriceAlert/index.js
index 806b652..bb9cbc4 100644
--- a/src/components/PriceAlert/index.js
+++ b/src/components/PriceAlert/index.js
@@ -1,10 +1,17 @@
import React, { Component } from 'react';
-import { ScrollView, StyleSheet, View, Text, TextInput } from 'react-native';
+import { ScrollView, StyleSheet, View, Text, TextInput, FlatList, Alert } from 'react-native';
import { connect } from 'react-redux';
import RadioForm, {RadioButton, RadioButtonInput, RadioButtonLabel} from 'react-native-simple-radio-button';
import { baseColor, lossColor, brandColor } from '../../config'
-
+import { Button, StyleProvider, List, ListItem, Body, Left, Right, Switch, Icon, Separator } from 'native-base';
+import getTheme from '../../../native-base-theme/components';
+import _platform from '../../../native-base-theme/variables/platform';
import { withDrawer } from '../../helpers/drawer';
+import { createPriceAlert } from '../../reducers/account'
+import { SimpleLineIcons, Entypo } from '@expo/vector-icons';
+import { NavigationActions } from 'react-navigation';
+import { getPriceAlert,disablePriceAlert, enablePriceAlert, deletePriceAlert } from '../../reducers/alert'
+
const styles = StyleSheet.create({
scrollContainer: {
@@ -13,10 +20,9 @@ const styles = StyleSheet.create({
container: {
display: 'flex',
borderColor: '#fff',
- justifyContent: 'space-between',
+ justifyContent: 'center',
backgroundColor: baseColor,
marginBottom: 10,
- flexDirection: 'row',
paddingLeft: 20
},
containerChild: {
@@ -48,95 +54,182 @@ const radioProps = [
{label: 'Continuous', value: 1 }
]
+const ShowPriceAlerts = ({alert, goToCreatePriceAlertPage, currencyName, price, currencySymbol, deletePriceAlert, switchAlert}) => (
+
+
+ {currencyName} Alerts
+
+ {
+ return (
+ deletePriceAlert(item.id)}>
+
+ {
+ (item.type == 0)?
+
+ :
+
+ }
+
+
+ {item.tsym} {item.price}
+
+ {
+ (item.type == 0)?
+ Greater Than
+ :
+ Less Than
+ }
+
+ {
+ (item.frequency == 0)?
+ Once
+ :
+ Persistent
+ }
+
+
+
+ {switchAlert(item)}} />
+
+
+ )
+ }}
+ data={alert}
+ keyExtractor={(item) => item.id}
+ />
+
+
+
+
+
+)
+
+const ShowCreateAlerts = ({goToCreatePriceAlertPage, currencyName, price, currencySymbol}) => (
+
+
+ You haven't created any Bitcoin price alerts
+ Create alerts to let you know when the price changes
+
+
+
+
+)
+
class PriceAlert extends Component {
state = {
- radioValue: 0
+ radioValue: 0,
+ gt: 0,
+ lt: 0,
+ frequency: 0,
+ alerts: [],
}
- render() {
- const { tokenDetails } = this.props
- return (
-
-
- THRESHOLD
-
+ componentWillMount = async() => {
+ const { getPriceAlert, alert } = this.props;
+ getPriceAlert();
+ }
-
- Above
- this.setState({text})}
- keyboardType='numeric'
- placeholder={'Enter numeric value'}
- placeholderTextColor='#444'
- />
-
+ switchAlert = async(item) => {
+ const { enablePriceAlert,disablePriceAlert } = this.props;
+ if(item.status){
+ disablePriceAlert(item.id)
+ return
+ }
+ enablePriceAlert(item.id)
+ }
-
- Current
- this.setState({text})}
- keyboardType='numeric'
- placeholder={''}
- placeholderTextColor='#444'
- />
-
+ deletePriceAlert = (alertId) => {
+ const { deletePriceAlert } = this.props;
+ Alert.alert(
+ 'Delete Alert',
+ 'Will you like to delete this Price Alert ?',
+ [
+ {text: 'Cancel', onPress: () => {}},
+ {text: 'OK', onPress: () => {
+ deletePriceAlert(alertId)
+ }},
+ ],
+ { cancelable: true }
+ )
+ }
-
- Below
- this.setState({text})}
- keyboardType='numeric'
- placeholder={'Enter Numeric value'}
- placeholderTextColor='#444'
- />
-
+ componentWillReceiveProps = (nextProps) => {
+ console.log('getDerivedStatefromprops')
+ console.log(nextProps)
+ const alerts = nextProps.alert.filter((item) => item.fsym == this.props.currencySymbol)
+ this.setState({alerts})
+ }
-
- FREQUENCY
-
+ render() {
+ let { goToCreatePriceAlertPage, currencyName, price, currencySymbol } = this.props
-
- {this.setState({radioValue: value})}}
+ return (
+
+
+
+ {
+ (this.state.alerts.length == 0 ) ?
+
-
+ :
+
+ }
+
+
+
)
}
}
-
const mapStateToProps = (state, props) => ({
tokenDetails: state.account.tokenDetails,
portfolio: state.account.portfolio,
+ alert: state.alert,
+ ...props.navigation.state.params,
+})
+
+const mapDispatchToProps = (dispatch) => ({
+ goToCreatePriceAlertPage: ({price, currencySymbol, currencyName, edit=false}) => dispatch(
+ NavigationActions.navigate({ routeName: 'Create Price Alert',
+ params: {price, currencySymbol, currencyName, edit} })),
+ getPriceAlert: () => dispatch(getPriceAlert()),
+ disablePriceAlert: (alertId)=>dispatch(disablePriceAlert(alertId)),
+ enablePriceAlert: (alertId)=>dispatch(enablePriceAlert(alertId)),
+ deletePriceAlert: (alertId) => dispatch(deletePriceAlert(alertId)),
+
})
-export default connect(mapStateToProps)(withDrawer(PriceAlert));
\ No newline at end of file
+export default connect(mapStateToProps, mapDispatchToProps)(withDrawer(PriceAlert));
\ No newline at end of file
diff --git a/src/components/TokenDetails/index.js b/src/components/TokenDetails/index.js
index 77b9146..82e245a 100644
--- a/src/components/TokenDetails/index.js
+++ b/src/components/TokenDetails/index.js
@@ -277,7 +277,7 @@ class TokenDetails extends Component {
platform: 'ethereum',
action: 'recieve',
contractAddress: tokenDetails.address,
- currencyName:tokenDetails.name,
+ currencyName: tokenDetails.name,
currencySymbol: tokenDetails.symbol,
image
},
@@ -290,6 +290,19 @@ class TokenDetails extends Component {
icon: 'eye',
Component: SimpleLineIcons,
onPress: () =>{console.log('onPress watch', isWatching); isWatching ? removeFromWatchList(symbol, token) : addToWatchlist(symbol, token) }
+ },
+ {
+ name: "Price Alert",
+ icon: 'bell',
+ params: {
+ contractAddress: tokenDetails.address,
+ currencyName: tokenDetails.name,
+ currencySymbol: tokenDetails.symbol,
+ price: tokenDetails.price,
+ image
+ },
+ Component: SimpleLineIcons,
+ route: "Price Alert"
}
]
diff --git a/src/config.js b/src/config.js
index 34055cd..4f164f8 100644
--- a/src/config.js
+++ b/src/config.js
@@ -1,7 +1,9 @@
import { ENVIRONMENT } from 'react-native-dotenv';
import { Constants } from 'expo'
-const devUrl = Constants.isDevice ? 'https://api.tokens.express' : 'http://dev.local:8888'
+// const devUrl = Constants.isDevice ? 'https://api.tokens.express' : 'http://dev.local:8888'
+const devUrl = Constants.isDevice ? 'https://api.tokens.express' : 'http://0.0.0.0:3000'
+
console.log({ENVIRONMENT})
export const baseURL = ENVIRONMENT !== 'development' ? 'https://api.tokens.express' : devUrl
diff --git a/src/helpers/api.js b/src/helpers/api.js
index 0236c63..67ebb0f 100644
--- a/src/helpers/api.js
+++ b/src/helpers/api.js
@@ -210,8 +210,30 @@ export const disableTwoFactorAuth = async (id) => {
return res.data
}
+export const setPriceAlert = async({fsym, tsym, price , type, frequency}) => {
+ const res = await instance.post(`/Alerts`, {fsym, tsym, price , type, frequency})
+ return res.data
+}
+
+export const getPriceAlert = async() => {
+ const res = await instance.get(`/Alerts`)
+ return res.data
+}
+
+export const enablePriceAlert = async(alertId) => {
+ const res = await instance.post(`/Alerts/enable`,{alertId})
+ return res.data
+}
+export const disablePriceAlert = async(alertId) => {
+ const res = await instance.post(`/Alerts/disable`,{alertId})
+ return res.data
+}
+export const deletePriceAlert = async(alertId) => {
+ const res = await instance.delete(`/Alerts/${alertId}`)
+ return res.data
+}
const log = (level) => (message, data) => {
console.log({ message, data, level })
diff --git a/src/helpers/drawer.js b/src/helpers/drawer.js
index 30b92a2..6bc2b28 100644
--- a/src/helpers/drawer.js
+++ b/src/helpers/drawer.js
@@ -172,13 +172,13 @@ export const withDrawer = (WrappedComponent) => {
'Token Details', 'Search', 'Price Alert', 'Add Address',
'ICO List', 'ICODetail', 'Education', 'Restore Wallet', 'New Wallet', 'Confirm Phrase', 'SetPin',
'SecuritySettings', 'Select Account', 'SendTransaction', 'Edit Profile', 'NewExchangeAccount', 'NewExchangeOrder',
- 'Confirm 2FA', '2FA', 'Set Currency', 'SignUp'
+ 'Confirm 2FA', '2FA', 'Set Currency', 'SignUp', 'Create Price Alert',
].indexOf(navState.routeName) > -1
const noSearchButton = [
'Restore Wallet', 'New Wallet', 'Confirm Phrase', 'SignUp', 'Login',
'SecuritySettings', 'Select Account', 'SendTransaction', 'Edit Profile', 'NewExchangeAccount',
- 'NewExchangeOrder', 'Confirm 2FA', '2FA'
+ 'NewExchangeOrder', 'Confirm 2FA', '2FA', 'Price Alert', 'Create Price Alert',
].indexOf(navState.routeName) > -1
// add top padding for iphone X
diff --git a/src/navigators/AppNavigator.js b/src/navigators/AppNavigator.js
index c9041b3..e33ed23 100644
--- a/src/navigators/AppNavigator.js
+++ b/src/navigators/AppNavigator.js
@@ -22,6 +22,7 @@ import Register from '../components/Register';
import SignUp from '../components/Register/SignUp';
import Login from '../components/Register/Login';
import PriceAlert from '../components/PriceAlert';
+import CreatePriceAlert from '../components/PriceAlert/create';
import EditProfile from '../components/Profile/EditProfile';
import SetCurrency from '../components/Profile/SetCurrency';
import CardStackStyleInterpolator from 'react-navigation/src/views/CardStack/CardStackStyleInterpolator';
@@ -76,6 +77,7 @@ export const Routes = {
'NewExchangeAccount': { screen: NewExchangeAccount },
'NewExchangeOrder': { screen: NewExchangeOrder },
'Price Alert': { screen: PriceAlert },
+ 'Create Price Alert': { screen: CreatePriceAlert },
'Profile': Profile,
'Settings': {screen: Settings},
'SecuritySettings': {screen: SecuritySettings},
diff --git a/src/reducers/account.js b/src/reducers/account.js
index acacb3f..9a8d3d9 100644
--- a/src/reducers/account.js
+++ b/src/reducers/account.js
@@ -25,7 +25,8 @@ import {
removeFromAccountWatchlist,
logger,
setCurrency,
- verifyTwoFactorAuth
+ verifyTwoFactorAuth,
+
} from '../helpers/api'
import {
genericError,
@@ -207,7 +208,7 @@ export const login = (params, supressToasts) => async (dispatch, getState) => {
} catch(err) {
const error = getError(err)
if(error && error.statusCode === 401) {
- error.message = 'Invalid cedentials';
+ error.message = 'Invalid credentials';
} else {
dispatch(NavigationActions.navigate({ routeName: 'Register' }))
}
diff --git a/src/reducers/alert.js b/src/reducers/alert.js
new file mode 100644
index 0000000..d6780df
--- /dev/null
+++ b/src/reducers/alert.js
@@ -0,0 +1,142 @@
+import { setLoading, showToast } from './ui'
+import { NavigationActions } from 'react-navigation'
+import types from '../types/alert';
+import {createReducer} from './common';
+
+import {
+ genericError,
+ getErrorMsg,
+ registerForPushNotificationsAsync,
+ safeAlert,
+ removeArrItem
+} from '../helpers/functions'
+import {
+ getPriceAlert as getPriceAlertApi,
+ setPriceAlert as setPriceAlertApi,
+ enablePriceAlert as enablePriceAlertApi,
+ disablePriceAlert as disablePriceAlertApi,
+ deletePriceAlert as deletePriceAlertApi,
+ setAuthHeader,
+} from '../helpers/api'
+import { SecureStore } from 'expo'
+import { fetchPriceAlertAction, disablePriceAlertAction, enablePriceAlertAction, deletePriceAlertAction } from '../actions/alert';
+
+const initialState = {
+ alerts: []
+}
+
+export const getPriceAlert = () => async(dispatch, getState) => {
+ let err = null
+
+ dispatch(setLoading(true, 'Fetching Price Alert'))
+ let token = await SecureStore.getItemAsync('token')
+ setAuthHeader('GnR32xxVUfaFX0GDYDG9mLYO1ARwncGPuHJRrLge9YSnWZsaDiaFyPCWGkmSxyPx')
+ const result = await getPriceAlertApi().catch(e=>err=e)
+ if(err){
+ dispatch(showToast(getErrorMsg(err)))
+ return
+ }
+
+ dispatch(fetchPriceAlertAction(result))
+ dispatch(setLoading(false))
+ dispatch(showToast('SuccessFul'))
+}
+
+export const createPriceAlert = (data) => async(dispatch, getState)=> {
+ let err = null
+ dispatch(setLoading(true, 'Creating Price Alert'))
+ let token = await SecureStore.getItemAsync('token')
+ // setAuthHeader(token)
+ setAuthHeader('GnR32xxVUfaFX0GDYDG9mLYO1ARwncGPuHJRrLge9YSnWZsaDiaFyPCWGkmSxyPx')
+ const setResult = await setPriceAlertApi(data).catch(e=>err=e)
+ if(err){
+ dispatch(setLoading(false))
+ dispatch(showToast(getErrorMsg(err)))
+ return
+ }
+ const result = await getPriceAlertApi().catch(e=>err=e)
+ if(err){
+ dispatch(showToast(getErrorMsg(err)))
+ return
+ }
+
+ dispatch(fetchPriceAlertAction(result))
+ dispatch(setLoading(false))
+ dispatch(showToast('Price Alert Created Successfully'))
+ dispatch(NavigationActions.back())
+}
+
+export const enablePriceAlert = (alertId) => async(dispatch, getState) => {
+ let err = null
+
+ let alerts = getState().alert;
+ const index = alerts.findIndex((item)=>item.id === alertId)
+ alerts[index].status = true
+
+ dispatch(setLoading(true, 'Enabling Price Alert'))
+ let token = await SecureStore.getItemAsync('token')
+ setAuthHeader('GnR32xxVUfaFX0GDYDG9mLYO1ARwncGPuHJRrLge9YSnWZsaDiaFyPCWGkmSxyPx')
+
+ const result = await enablePriceAlertApi(alertId).catch(e=>err=e)
+ if(err){
+ dispatch(setLoading(false))
+ dispatch(showToast(getErrorMsg(err)))
+ return
+ }
+ dispatch(setLoading(false))
+ dispatch(enablePriceAlertAction(alerts))
+}
+
+export const disablePriceAlert = (alertId) => async(dispatch, getState) => {
+ let err = null
+
+ dispatch(setLoading(true, 'Disabling Price Alert'))
+
+ let alerts = getState().alert;
+ const index = alerts.findIndex((item)=>item.id === alertId)
+ alerts[index].status = false
+
+ let token = await SecureStore.getItemAsync('token')
+ setAuthHeader('GnR32xxVUfaFX0GDYDG9mLYO1ARwncGPuHJRrLge9YSnWZsaDiaFyPCWGkmSxyPx')
+
+ const result = await disablePriceAlertApi(alertId).catch(e=>err=e)
+ if(err){
+ dispatch(setLoading(false))
+ dispatch(showToast(getErrorMsg(err)))
+ return
+ }
+ dispatch(setLoading(false))
+ dispatch(disablePriceAlertAction(alerts))
+}
+
+export const deletePriceAlert = (alertId) => async(dispatch, getState) => {
+ let err = null
+
+ dispatch(setLoading(true, 'Deleting Price Alert'))
+
+ let alerts = getState().alert;
+ const index = alerts.findIndex((item)=>item.id === alertId)
+ alerts = alerts.splice(index, 1)
+
+ let token = await SecureStore.getItemAsync('token')
+ setAuthHeader('GnR32xxVUfaFX0GDYDG9mLYO1ARwncGPuHJRrLge9YSnWZsaDiaFyPCWGkmSxyPx')
+
+ const result = await deletePriceAlertApi(alertId).catch(e=>err=e)
+ if(err){
+ dispatch(setLoading(false))
+ dispatch(showToast(getErrorMsg(err)))
+ return
+ }
+ dispatch(setLoading(false))
+ dispatch(deletePriceAlertAction(alerts))
+
+}
+
+export const actionHandlers = {
+ [types.FETCH_PRICE_ALERT]: (state, data) => { return [ ...data ] },
+ [types.ENABLE_PRICE_ALERT]: (state, data) => {return [...data ] },
+ [types.DISABLE_PRICE_ALERT]: (state, data) => { return [...data]},
+ [types.DELETE_PRICE_ALERT]: (state, data) => { return [...data]},
+};
+
+export default createReducer(initialState, actionHandlers);
diff --git a/src/reducers/index.js b/src/reducers/index.js
index b450bde..1a35a94 100644
--- a/src/reducers/index.js
+++ b/src/reducers/index.js
@@ -9,6 +9,7 @@ import ticker from './ticker'
import security from './security'
import blockchains from './blockchains'
import exchanges from './exchanges'
+import alert from './alert'
import { AppNavigator } from '../navigators/AppNavigator';
@@ -58,7 +59,8 @@ const AppReducer = combineReducers({
ticker,
security,
blockchains,
- exchanges
+ exchanges,
+ alert,
});
export default AppReducer;
diff --git a/src/types/alert.js b/src/types/alert.js
new file mode 100644
index 0000000..6527735
--- /dev/null
+++ b/src/types/alert.js
@@ -0,0 +1,6 @@
+export default {
+ ENABLE_PRICE_ALERT: 'ENABLE_PRICE_ALERT',
+ DISABLE_PRICE_ALERT: 'DISABLE_PRICE_ALERT',
+ DELETE_PRICE_ALERT: 'DELETE_PRICE_ALERT',
+ FETCH_PRICE_ALERT: 'FETCH_PRICE_ALERT',
+}
\ No newline at end of file