mirror of
https://github.com/ecency/ecency-mobile.git
synced 2024-12-20 11:51:52 +03:00
Merge branch 'development' into nt/post-pinning
This commit is contained in:
commit
1887765dc9
@ -85,6 +85,7 @@
|
||||
"react-native-animatable": "^1.3.3",
|
||||
"react-native-autoheight-webview": "^1.5.8",
|
||||
"react-native-camera": "^4.2.1",
|
||||
"react-native-chart-kit": "^6.11.0",
|
||||
"react-native-config": "luggit/react-native-config#master",
|
||||
"react-native-crypto": "^2.2.0",
|
||||
"react-native-date-picker": "^3.2.7",
|
||||
|
@ -25,6 +25,7 @@ import { useIntl } from 'react-intl';
|
||||
import { useAppSelector } from '../../../hooks';
|
||||
import { getUnreadNotificationCount } from '../../../providers/ecency/ecency';
|
||||
import { decryptKey } from '../../../utils/crypto';
|
||||
import { getUser as getEcencyUser} from '../../../providers/ecency/ePoint';
|
||||
|
||||
const AccountsBottomSheetContainer = ({ navigation }) => {
|
||||
const intl = useIntl();
|
||||
@ -106,6 +107,7 @@ const AccountsBottomSheetContainer = ({ navigation }) => {
|
||||
decryptKey(encryptedAccessToken, getDigitPinCode(pinHash))
|
||||
);
|
||||
_currentAccount.mutes = await getMutes(_currentAccount.username);
|
||||
_currentAccount.ecencyUserData = await getEcencyUser(_currentAccount.username);
|
||||
dispatch(updateCurrentAccount(_currentAccount));
|
||||
}
|
||||
|
||||
|
@ -1 +1,2 @@
|
||||
export * from './optionsModal';
|
||||
export * from './optionsModal';
|
||||
export * from './refreshControl';
|
1
src/components/atoms/refreshControl/index.ts
Normal file
1
src/components/atoms/refreshControl/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './refreshControl';
|
23
src/components/atoms/refreshControl/refreshControl.tsx
Normal file
23
src/components/atoms/refreshControl/refreshControl.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
import { RefreshControl as RNRefreshControl } from 'react-native';
|
||||
import { ThemeContainer } from '../../../containers';
|
||||
|
||||
export interface RefreshControlProps {
|
||||
refreshing:boolean,
|
||||
onRefresh:()=>void
|
||||
}
|
||||
|
||||
export const RefreshControl = ({refreshing, onRefresh }:RefreshControlProps) => (
|
||||
<ThemeContainer>
|
||||
{({ isDarkTheme }) => (
|
||||
<RNRefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={onRefresh}
|
||||
progressBackgroundColor="#357CE6"
|
||||
tintColor={!isDarkTheme ? '#357ce6' : '#96c0ff'}
|
||||
titleColor="#fff"
|
||||
colors={['#fff']}
|
||||
/>
|
||||
)}
|
||||
</ThemeContainer>
|
||||
);
|
@ -96,6 +96,7 @@ import QuickReplyModal from './quickReplyModal/quickReplyModalView';
|
||||
import Tooltip from './tooltip/tooltipView';
|
||||
import VideoPlayer from './videoPlayer/videoPlayerView';
|
||||
import QRModal from './qrModal/qrModalView';
|
||||
import { SimpleChart } from './simpleChart';
|
||||
|
||||
// Basic UI Elements
|
||||
import {
|
||||
@ -240,4 +241,5 @@ export {
|
||||
Tooltip,
|
||||
VideoPlayer,
|
||||
QRModal,
|
||||
SimpleChart,
|
||||
};
|
||||
|
@ -24,6 +24,7 @@ export default EStyleSheet.create({
|
||||
color: 'white',
|
||||
fontWeight: 'bold',
|
||||
alignSelf: 'center',
|
||||
textAlign: 'center',
|
||||
fontSize: 14,
|
||||
paddingLeft: 10,
|
||||
paddingRight: 10,
|
||||
|
@ -34,7 +34,8 @@ export default EStyleSheet.create({
|
||||
borderWidth: 1,
|
||||
borderColor: '$borderColor',
|
||||
borderRadius: 8,
|
||||
padding: 10,
|
||||
paddingVertical: 10,
|
||||
paddingHorizontal: 12,
|
||||
color: '$primaryBlack',
|
||||
width: 172,
|
||||
},
|
||||
@ -110,7 +111,7 @@ export default EStyleSheet.create({
|
||||
},
|
||||
dropdownText: {
|
||||
fontSize: 14,
|
||||
paddingLeft: 16,
|
||||
paddingLeft: 12,
|
||||
paddingHorizontal: 14,
|
||||
color: '$primaryDarkGray',
|
||||
},
|
||||
@ -130,7 +131,7 @@ export default EStyleSheet.create({
|
||||
},
|
||||
dropdown: {
|
||||
flexGrow: 1,
|
||||
width: 150,
|
||||
width: 130,
|
||||
},
|
||||
slider: {
|
||||
flex: 1,
|
||||
|
@ -33,7 +33,8 @@ export default EStyleSheet.create({
|
||||
borderWidth: 1,
|
||||
borderColor: '$borderColor',
|
||||
borderRadius: 8,
|
||||
padding: 10,
|
||||
paddingVertical: 10,
|
||||
paddingHorizontal: 12,
|
||||
color: '$primaryBlack',
|
||||
width: 172,
|
||||
},
|
||||
@ -108,7 +109,7 @@ export default EStyleSheet.create({
|
||||
},
|
||||
dropdownText: {
|
||||
fontSize: 14,
|
||||
paddingLeft: 16,
|
||||
paddingLeft: 12,
|
||||
paddingHorizontal: 14,
|
||||
color: '$primaryDarkGray',
|
||||
},
|
||||
@ -128,7 +129,7 @@ export default EStyleSheet.create({
|
||||
},
|
||||
dropdown: {
|
||||
flexGrow: 1,
|
||||
width: 150,
|
||||
width: 130,
|
||||
},
|
||||
slider: {
|
||||
flex: 1,
|
||||
|
1
src/components/simpleChart/index.ts
Normal file
1
src/components/simpleChart/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './simpleChart';
|
50
src/components/simpleChart/simpleChart.tsx
Normal file
50
src/components/simpleChart/simpleChart.tsx
Normal file
@ -0,0 +1,50 @@
|
||||
import React from 'react'
|
||||
import { LineChart } from 'react-native-chart-kit'
|
||||
import EStyleSheet from 'react-native-extended-stylesheet';
|
||||
|
||||
interface CoinChartProps {
|
||||
data:number[],
|
||||
baseWidth:number,
|
||||
chartHeight:number,
|
||||
showLine:boolean,
|
||||
showLabels?:boolean,
|
||||
}
|
||||
|
||||
export const SimpleChart = ({data, baseWidth, chartHeight, showLine, showLabels = false}:CoinChartProps) => {
|
||||
|
||||
if(!data || !data.length){
|
||||
return null;
|
||||
}
|
||||
|
||||
const _chartWidth = baseWidth + baseWidth/(data.length -1)
|
||||
const _chartBackgroundColor = EStyleSheet.value('$primaryLightBackground');
|
||||
return (
|
||||
<LineChart
|
||||
data={{
|
||||
labels: [],
|
||||
datasets: [
|
||||
{
|
||||
data
|
||||
}
|
||||
],
|
||||
}}
|
||||
width={_chartWidth} // from react-native
|
||||
height={chartHeight}
|
||||
withHorizontalLabels={showLabels}
|
||||
withVerticalLabels={false}
|
||||
withHorizontalLines={false}
|
||||
withDots={false}
|
||||
withInnerLines={false}
|
||||
chartConfig={{
|
||||
backgroundColor:_chartBackgroundColor,
|
||||
backgroundGradientFrom: _chartBackgroundColor,
|
||||
backgroundGradientTo: _chartBackgroundColor,
|
||||
fillShadowGradient: EStyleSheet.value('$chartBlue'),
|
||||
fillShadowGradientOpacity:0.8,
|
||||
labelColor:() => EStyleSheet.value('$primaryDarkText'),
|
||||
color: () => showLine?EStyleSheet.value('$chartBlue'):'transparent',
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@ -21,7 +21,10 @@ const TransactionView = ({ item, index }) => {
|
||||
text={intl.formatMessage({
|
||||
id: `wallet.${get(item, 'textKey')}`,
|
||||
})}
|
||||
description={getTimeFromNow(get(item, 'created'))}
|
||||
description={
|
||||
(item.expires ? intl.formatMessage({ id: 'wallet.expires' }) + ' ' : '') +
|
||||
getTimeFromNow(item.expires || item.created)
|
||||
}
|
||||
isCircleIcon
|
||||
isThin
|
||||
circleIconColor="white"
|
||||
|
12
src/config/coingeckoApi.ts
Normal file
12
src/config/coingeckoApi.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import axios from 'axios';
|
||||
|
||||
const BASE_URL = 'https://api.coingecko.com';
|
||||
const PATH_API = 'api';
|
||||
const API_VERSION = 'v3';
|
||||
|
||||
const coingeckoApi = axios.create({
|
||||
baseURL: `${BASE_URL}/${PATH_API}/${API_VERSION}`,
|
||||
});
|
||||
|
||||
|
||||
export default coingeckoApi;
|
@ -25,6 +25,7 @@ api.interceptors.request.use((request) => {
|
||||
|| request.url.startsWith('private-api/leaderboard')
|
||||
|| request.url.startsWith('/private-api/received-vesting/')
|
||||
|| request.url.startsWith('/private-api/referrals/')
|
||||
|| request.url.startsWith('/private-api/market-data')
|
||||
){
|
||||
return request
|
||||
}
|
||||
|
@ -7,7 +7,9 @@
|
||||
"transfer": "Transfer",
|
||||
"power_up": "To Vesting",
|
||||
"transfer_from_savings": "From Savings",
|
||||
"withdraw_savings":"Withdraw Savings",
|
||||
"withdraw_vesting": "Power Down",
|
||||
"open_order":"Open Order",
|
||||
"fill_order": "Fill Order",
|
||||
"post": "Post",
|
||||
"comment": "Comment",
|
||||
@ -49,7 +51,12 @@
|
||||
"to": "To",
|
||||
"estimated_value_desc": "According to purchase value",
|
||||
"estimated_value": "Estimated value",
|
||||
"estimated_amount": "Vote value",
|
||||
"vote_value": "Vote value",
|
||||
"delegated_hive_power":"Delegated hive power",
|
||||
"powering_down_hive_power":"Powering down",
|
||||
"received_hive_power":"Received hive power",
|
||||
"total_hive_power":"Total hive power",
|
||||
"savings": "Savings",
|
||||
"amount_information": "Drag the slider to adjust the amount",
|
||||
"amount": "Amount",
|
||||
"memo": "Memo",
|
||||
@ -68,10 +75,11 @@
|
||||
"next": "NEXT",
|
||||
"delegate": "Delegate",
|
||||
"power_down": "Power Down",
|
||||
"withdraw_hive": "Withdraw HIVE",
|
||||
"withdraw_hbd": "Withdraw HBD",
|
||||
"withdraw_hive": "Withdraw Savings",
|
||||
"withdraw_hbd": "Withdraw Savings",
|
||||
"transfer_to_savings": "To Savings",
|
||||
"convert": "Convert",
|
||||
"convert_request":"Convert Request",
|
||||
"escrow_transfer": "Escrow Transfer",
|
||||
"escrow_dispute": "Escrow Dispute",
|
||||
"escrow_release": "Escrow Release",
|
||||
@ -81,8 +89,9 @@
|
||||
"fill_convert_request": "Convert Executed",
|
||||
"fill_transfer_from_savings": "Savings Executed",
|
||||
"fill_vesting_withdraw": "PowerDown executed",
|
||||
"estm": {
|
||||
"ecency": {
|
||||
"title": "Points",
|
||||
"name":"Ecency Points",
|
||||
"buy": "GET POINTS"
|
||||
},
|
||||
"savinghive": {
|
||||
@ -93,6 +102,7 @@
|
||||
},
|
||||
"hive": {
|
||||
"title": "HIVE",
|
||||
"name": "Hive Token",
|
||||
"buy": "GET HIVE"
|
||||
},
|
||||
"hbd": {
|
||||
@ -100,13 +110,28 @@
|
||||
"buy": "GET HBD"
|
||||
},
|
||||
"hive_power": {
|
||||
"title": "HIVE POWER"
|
||||
"title": "HIVE POWER",
|
||||
"name":"Hive Power"
|
||||
|
||||
},
|
||||
"hive_dollar":{
|
||||
"name":"Hive Dollar"
|
||||
},
|
||||
"btc": {
|
||||
"title": "BTC",
|
||||
"buy": "GET BTC",
|
||||
"address": "RECEIVE"
|
||||
}
|
||||
},
|
||||
"last_updated":"Last Updated:",
|
||||
"updating":"Updating...",
|
||||
"coin_details":"Details",
|
||||
"change":"Change",
|
||||
"activities":"Activities",
|
||||
"savings_withdrawal":"Pending Withdrawals",
|
||||
"open_orders":"Open Orders",
|
||||
"conversions_requested":"Conversions Requested",
|
||||
"expires":"expires",
|
||||
"pending_requests":"Pending Requests"
|
||||
},
|
||||
"notification": {
|
||||
"vote": "voted",
|
||||
@ -445,7 +470,9 @@
|
||||
"failed_to_open": "Failed to open a link",
|
||||
"restart_ecency": "Restart Ecency?",
|
||||
"restart_ecency_desc": "Applying changes will require a restart.",
|
||||
"invalid_response":"Could not process request, Try again later."
|
||||
"invalid_response":"Could not process request, Try again later.",
|
||||
"wallet_updating":"Wallet update in progress, try again as update finishes",
|
||||
"claim_failed":"Failed to claim rewards, {message}\nTry again or write to support@ecency.com"
|
||||
},
|
||||
"post": {
|
||||
"reblog_alert": "Are you sure, you want to reblog?",
|
||||
|
29
src/constants/defaultCoins.ts
Normal file
29
src/constants/defaultCoins.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { CoinBase } from "../redux/reducers/walletReducer"
|
||||
|
||||
const DEFAULT_COINS = [{
|
||||
id:'ecency',
|
||||
symbol:'Points',
|
||||
notCrypto:true
|
||||
},{
|
||||
id:'hive_power',
|
||||
symbol:'HP',
|
||||
notCrypto:true
|
||||
},{
|
||||
id:'hive',
|
||||
symbol:'HIVE',
|
||||
notCrypto:false
|
||||
},{
|
||||
id:'hive_dollar',
|
||||
symbol:'HBD',
|
||||
notCrypto:false
|
||||
}] as CoinBase[]
|
||||
|
||||
export const COIN_IDS = {
|
||||
ECENCY:'ecency',
|
||||
HIVE:'hive',
|
||||
HBD:'hive_dollar',
|
||||
HP:'hive_power'
|
||||
}
|
||||
|
||||
|
||||
export default DEFAULT_COINS
|
@ -33,6 +33,7 @@ export default {
|
||||
WEB_BROWSER: `WebBrowser${SCREEN_SUFFIX}`,
|
||||
REFER: `Refer${SCREEN_SUFFIX}`,
|
||||
QR: `QR${SCREEN_SUFFIX}`,
|
||||
COIN_DETAILS: `CoinDetails${SCREEN_SUFFIX}`,
|
||||
},
|
||||
DRAWER: {
|
||||
MAIN: `Main${DRAWER_SUFFIX}`,
|
||||
|
@ -24,6 +24,8 @@ import { getUser } from '../providers/ecency/ePoint';
|
||||
// Utils
|
||||
import { countDecimals } from '../utils/number';
|
||||
import bugsnagInstance from '../config/bugsnag';
|
||||
import { fetchCoinsData } from '../utils/wallet';
|
||||
import { fetchAndSetCoinsData } from '../redux/actions/walletActions';
|
||||
|
||||
/*
|
||||
* Props Name Description Value
|
||||
@ -78,7 +80,7 @@ class TransferContainer extends Component {
|
||||
(transferType === 'purchase_estm' || transferType === 'transfer_token') &&
|
||||
fundType === 'HIVE'
|
||||
) {
|
||||
balance = account[0].balance.replace(fundType, '');
|
||||
balance = account.balance.replace(fundType, '');
|
||||
}
|
||||
if (
|
||||
(transferType === 'purchase_estm' ||
|
||||
@ -86,19 +88,19 @@ class TransferContainer extends Component {
|
||||
transferType === 'transfer_token') &&
|
||||
fundType === 'HBD'
|
||||
) {
|
||||
balance = account[0].hbd_balance.replace(fundType, '');
|
||||
balance = account.hbd_balance.replace(fundType, '');
|
||||
}
|
||||
if (transferType === 'points' && fundType === 'ESTM') {
|
||||
this._getUserPointsBalance(username);
|
||||
}
|
||||
if (transferType === 'transfer_to_savings' && fundType === 'HIVE') {
|
||||
balance = account[0].balance.replace(fundType, '');
|
||||
balance = account.balance.replace(fundType, '');
|
||||
}
|
||||
if (transferType === 'transfer_to_savings' && fundType === 'HBD') {
|
||||
balance = account[0].hbd_balance.replace(fundType, '');
|
||||
balance = account.hbd_balance.replace(fundType, '');
|
||||
}
|
||||
if (transferType === 'transfer_to_vesting' && fundType === 'HIVE') {
|
||||
balance = account[0].balance.replace(fundType, '');
|
||||
balance = account.balance.replace(fundType, '');
|
||||
}
|
||||
if (transferType === 'address_view' && fundType === 'BTC') {
|
||||
//TODO implement transfer of custom tokens
|
||||
@ -112,7 +114,7 @@ class TransferContainer extends Component {
|
||||
}
|
||||
|
||||
this.setState({
|
||||
selectedAccount: { ...account[0], local: local[0] },
|
||||
selectedAccount: { ...account, local: local[0] },
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -122,6 +124,13 @@ class TransferContainer extends Component {
|
||||
return validUsers;
|
||||
};
|
||||
|
||||
_delayedRefreshCoinsData = () => {
|
||||
const { dispatch } = this.props;
|
||||
setTimeout(() => {
|
||||
dispatch(fetchAndSetCoinsData(true));
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
_transferToAccount = async (from, destination, amount, memo) => {
|
||||
const { pinCode, navigation, dispatch, intl } = this.props;
|
||||
let { currentAccount } = this.props;
|
||||
@ -192,6 +201,7 @@ class TransferContainer extends Component {
|
||||
return func(currentAccount, pinCode, data)
|
||||
.then(() => {
|
||||
dispatch(toastNotification(intl.formatMessage({ id: 'alert.successful' })));
|
||||
this._delayedRefreshCoinsData();
|
||||
navigation.goBack();
|
||||
})
|
||||
.catch((err) => {
|
||||
@ -218,6 +228,7 @@ class TransferContainer extends Component {
|
||||
|
||||
_handleOnModalClose = () => {
|
||||
const { navigation } = this.props;
|
||||
this._delayedRefreshCoinsData();
|
||||
navigation.goBack();
|
||||
};
|
||||
|
||||
|
@ -20,6 +20,7 @@ import { getEstimatedAmount } from '../utils/vote';
|
||||
|
||||
// Constants
|
||||
import ROUTES from '../constants/routeNames';
|
||||
import { COIN_IDS } from '../constants/defaultCoins';
|
||||
|
||||
const HIVE_DROPDOWN = [
|
||||
'purchase_estm',
|
||||
@ -36,6 +37,7 @@ const HIVE_POWER_DROPDOWN = ['delegate', 'power_down'];
|
||||
const WalletContainer = ({
|
||||
children,
|
||||
currentAccount,
|
||||
coinSymbol,
|
||||
globalProps,
|
||||
handleOnScroll,
|
||||
pinCode,
|
||||
@ -166,13 +168,13 @@ const WalletContainer = ({
|
||||
|
||||
getAccount(currentAccount.name)
|
||||
.then((account) => {
|
||||
isHasUnclaimedRewards = _isHasUnclaimedRewards(account[0]);
|
||||
isHasUnclaimedRewards = _isHasUnclaimedRewards(account);
|
||||
if (isHasUnclaimedRewards) {
|
||||
const {
|
||||
reward_hive_balance: hiveBal,
|
||||
reward_hbd_balance: hbdBal,
|
||||
reward_vesting_balance: vestingBal,
|
||||
} = account[0];
|
||||
} = account;
|
||||
return claimRewardBalance(currentAccount, pinCode, hiveBal, hbdBal, vestingBal);
|
||||
}
|
||||
setIsClaiming(false);
|
||||
@ -273,6 +275,26 @@ const WalletContainer = ({
|
||||
}
|
||||
};
|
||||
|
||||
//process symbol based data
|
||||
let balance = 0;
|
||||
let estimateValue = 0;
|
||||
let savings = 0;
|
||||
switch (coinSymbol) {
|
||||
case COIN_IDS.HIVE:
|
||||
balance = hiveBalance;
|
||||
estimateValue = estimatedHiveValue;
|
||||
savings = hiveSavingBalance;
|
||||
break;
|
||||
case COIN_IDS.HBD:
|
||||
balance = hbdBalance;
|
||||
estimateValue = estimatedHbdValue;
|
||||
savings = hbdSavingBalance;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
children &&
|
||||
children({
|
||||
@ -309,6 +331,11 @@ const WalletContainer = ({
|
||||
hivePowerDropdown: HIVE_POWER_DROPDOWN,
|
||||
unclaimedBalance: unclaimedBalance && unclaimedBalance.trim(),
|
||||
estimatedAmount,
|
||||
|
||||
//symbol based data
|
||||
balance,
|
||||
estimateValue,
|
||||
savings,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
@ -40,6 +40,7 @@ import {
|
||||
Communities,
|
||||
WebBrowser,
|
||||
ReferScreen,
|
||||
CoinDetails,
|
||||
} from '../screens';
|
||||
|
||||
const bottomTabNavigator = createBottomTabNavigator(
|
||||
@ -154,6 +155,7 @@ const stackNavigator = createStackNavigator(
|
||||
[ROUTES.SCREENS.COMMUNITIES]: { screen: Communities },
|
||||
[ROUTES.SCREENS.WEB_BROWSER]: { screen: WebBrowser },
|
||||
[ROUTES.SCREENS.REFER]: { screen: ReferScreen },
|
||||
[ROUTES.SCREENS.COIN_DETAILS]: { screen: CoinDetails },
|
||||
},
|
||||
{
|
||||
headerMode: 'none',
|
||||
|
42
src/providers/coingecko/coingecko.ts
Normal file
42
src/providers/coingecko/coingecko.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import bugsnagInstance from '../../config/bugsnag';
|
||||
import coingeckoApi from '../../config/coingeckoApi';
|
||||
import { convertMarketData } from './converters';
|
||||
import { MarketData } from './models';
|
||||
|
||||
|
||||
const PATH_COINS = 'coins';
|
||||
const PATH_MARKET_CHART = 'market_chart';
|
||||
|
||||
export const INTERVAL_HOURLY = 'hourly';
|
||||
export const INTERVAL_DAILY = 'daily';
|
||||
|
||||
export const fetchMarketChart = async (
|
||||
coingeckoId:string,
|
||||
vs_currency:string,
|
||||
days:number,
|
||||
interval:'daily'|'hourly' = 'daily'
|
||||
): Promise<MarketData> => {
|
||||
try{
|
||||
const params = {
|
||||
vs_currency,
|
||||
days,
|
||||
interval
|
||||
}
|
||||
|
||||
const res = await coingeckoApi.get(`/${PATH_COINS}/${coingeckoId}/${PATH_MARKET_CHART}`, {
|
||||
params
|
||||
});
|
||||
const rawData = res.data;
|
||||
if(!rawData){
|
||||
throw new Error("Tag name not available")
|
||||
}
|
||||
|
||||
const data = convertMarketData(rawData);
|
||||
|
||||
return data;
|
||||
|
||||
}catch(error){
|
||||
bugsnagInstance.notify(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
23
src/providers/coingecko/converters.ts
Normal file
23
src/providers/coingecko/converters.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { ChartItem, MarketData } from "./models"
|
||||
|
||||
export const convertChartItem = (rawData:any) => {
|
||||
if(!rawData){
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
xValue:rawData[0],
|
||||
yValue:rawData[1]
|
||||
} as ChartItem;
|
||||
}
|
||||
|
||||
export const convertMarketData = (rawData:any) => {
|
||||
if(!rawData){
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
prices:rawData.prices ? rawData.prices.map(convertChartItem) : [],
|
||||
marketCaps:rawData.market_caps ? rawData.market_caps.map(convertChartItem) : [],
|
||||
totalVolumes:rawData.total_volumes ? rawData.total_volumes.map(convertChartItem) : []
|
||||
} as MarketData;
|
||||
}
|
0
src/providers/coingecko/index.ts
Normal file
0
src/providers/coingecko/index.ts
Normal file
10
src/providers/coingecko/models.ts
Normal file
10
src/providers/coingecko/models.ts
Normal file
@ -0,0 +1,10 @@
|
||||
export interface ChartItem {
|
||||
xValue:number,
|
||||
yValue:number
|
||||
}
|
||||
|
||||
export interface MarketData {
|
||||
prices:Array<ChartItem>;
|
||||
marketCaps:Array<ChartItem>;
|
||||
totalVolumes:Array<ChartItem>;
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import { COIN_IDS } from '../../constants/defaultCoins';
|
||||
import { Referral } from '../../models';
|
||||
import { ReferralStat } from './ecency.types';
|
||||
import { LatestMarketPrices, LatestQuotes, QuoteItem, ReferralStat } from './ecency.types';
|
||||
|
||||
export const convertReferral = (rawData: any) => {
|
||||
return {
|
||||
@ -17,3 +18,28 @@ export const convertReferralStat = (rawData: any) => {
|
||||
rewarded: rawData.rewarded || 0,
|
||||
} as ReferralStat;
|
||||
};
|
||||
|
||||
export const convertQuoteItem = (rawData:any, currencyRate:number) => {
|
||||
if(!rawData){
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
price:rawData.price * currencyRate,
|
||||
percentChange:rawData.percent_change,
|
||||
lastUpdated:rawData.last_updated,
|
||||
} as QuoteItem
|
||||
}
|
||||
|
||||
export const convertLatestQuotes = (rawData: any, estmPrice:number, currencyRate:number) => {
|
||||
return {
|
||||
[COIN_IDS.HIVE]:convertQuoteItem(rawData.hive.quotes.usd, currencyRate),
|
||||
[COIN_IDS.HP]:convertQuoteItem(rawData.hive.quotes.usd, currencyRate),
|
||||
[COIN_IDS.HBD]:convertQuoteItem(rawData.hbd.quotes.usd, currencyRate),
|
||||
[COIN_IDS.ECENCY]:convertQuoteItem({
|
||||
price:estmPrice,
|
||||
percent_change:0,
|
||||
last_updated:new Date().toISOString()
|
||||
}, currencyRate)
|
||||
|
||||
} as LatestQuotes;
|
||||
};
|
||||
|
@ -2,6 +2,7 @@ import { Alert } from 'react-native';
|
||||
import ePointApi from '../../config/api';
|
||||
import ecencyApi from '../../config/ecencyApi';
|
||||
import bugsnagInstance from '../../config/bugsnag';
|
||||
import { EcencyUser, UserPoint } from './ecency.types';
|
||||
|
||||
|
||||
/**
|
||||
@ -32,7 +33,7 @@ export const userActivity = async (ty:number, tx:string = '', bl:string|number =
|
||||
}
|
||||
|
||||
|
||||
export const getUser = (username) =>
|
||||
export const getUser = (username:string):Promise<EcencyUser> =>
|
||||
new Promise((resolve) => {
|
||||
ePointApi
|
||||
.get(`/users/${username}`)
|
||||
@ -44,7 +45,7 @@ export const getUser = (username) =>
|
||||
});
|
||||
});
|
||||
|
||||
export const getUserPoints = (username) =>
|
||||
export const getUserPoints = (username:string):Promise<UserPoint[]> =>
|
||||
new Promise((resolve) => {
|
||||
ePointApi
|
||||
.get(`/users/${username}/points`)
|
||||
@ -62,7 +63,8 @@ export const claimPoints = async () => {
|
||||
return response.data;
|
||||
}catch(error){
|
||||
console.warn("Failed to calim points", error);
|
||||
bugsnagInstance.notify(error)
|
||||
bugsnagInstance.notify(error);
|
||||
throw new Error(error.response?.data?.message || error.message)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,8 +6,8 @@ import bugsnagInstance from '../../config/bugsnag';
|
||||
import { SERVER_LIST } from '../../constants/options/api';
|
||||
import { parsePost } from '../../utils/postParser';
|
||||
import { extractMetadata, makeJsonMetadata } from '../../utils/editor';
|
||||
import { ReceivedVestingShare, Referral, ReferralStat } from './ecency.types';
|
||||
import { convertReferral, convertReferralStat } from './converters';
|
||||
import { LatestMarketPrices, ReceivedVestingShare, Referral, ReferralStat } from './ecency.types';
|
||||
import { convertLatestQuotes, convertReferral, convertReferralStat } from './converters';
|
||||
|
||||
|
||||
|
||||
@ -27,6 +27,27 @@ export const getCurrencyRate = (currency) =>
|
||||
return 1;
|
||||
});
|
||||
|
||||
export const getLatestQuotes = async (currencyRate:number):Promise<LatestMarketPrices> => {
|
||||
try{
|
||||
console.log('using currency rate', currencyRate);
|
||||
const res = await ecencyApi.get(`/private-api/market-data/latest`);
|
||||
const estmRes = await getCurrencyTokenRate('usd','estm')
|
||||
|
||||
if(!res.data || !estmRes){
|
||||
throw new Error("No quote data returned");
|
||||
}
|
||||
|
||||
const data = convertLatestQuotes(res.data, estmRes, currencyRate);
|
||||
console.log('parsed quotes data', data);
|
||||
return data;
|
||||
} catch (error){
|
||||
bugsnagInstance.notify(error);
|
||||
console.warn(error);
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const getCurrencyTokenRate = (currency, token) =>
|
||||
api
|
||||
.get(`/market-data/currency-rate/${currency}/${token}`)
|
||||
@ -52,6 +73,8 @@ export const getReceivedVestingShares = async (username: string):Promise<Receive
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,9 +1,20 @@
|
||||
import { QuoteItem } from "../../redux/reducers/walletReducer";
|
||||
|
||||
export interface ReceivedVestingShare {
|
||||
delegator:string;
|
||||
delegatee:string;
|
||||
vesting_shares:string;
|
||||
timestamp:string;
|
||||
}
|
||||
|
||||
export interface EcencyUser {
|
||||
username:string;
|
||||
points:string;
|
||||
unclaimed_points:string;
|
||||
points_by_type:{[key:string]:string};
|
||||
unclaimed_points_by_type:{[key:string]:string};
|
||||
}
|
||||
|
||||
export interface Referral {
|
||||
id:number;
|
||||
referral:string;
|
||||
@ -15,4 +26,19 @@ export interface Referral {
|
||||
export interface ReferralStat {
|
||||
total: number;
|
||||
rewarded: number;
|
||||
}
|
||||
}
|
||||
|
||||
export interface UserPoint {
|
||||
id: number;
|
||||
type: number;
|
||||
amount: string;
|
||||
created:string;
|
||||
memo?: string;
|
||||
receiver?: string;
|
||||
sender?: string;
|
||||
|
||||
}
|
||||
|
||||
export interface LatestQuotes {
|
||||
[key:string]:QuoteItem
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import get from 'lodash/get';
|
||||
|
||||
import { Alert } from 'react-native';
|
||||
import { getDigitPinCode, getMutes, getUser } from './dhive';
|
||||
import { getUser as getEcencyUser } from '../ecency/ePoint';
|
||||
import {
|
||||
setUserData,
|
||||
setAuthStatus,
|
||||
@ -71,6 +72,7 @@ export const login = async (username, password, isPinCodeOpen) => {
|
||||
scTokens ? scTokens.access_token : '',
|
||||
);
|
||||
account.mutes = await getMutes(account.username);
|
||||
account.ecencyUserData = await getEcencyUser(account.username);
|
||||
|
||||
let jsonMetadata;
|
||||
try {
|
||||
@ -137,6 +139,7 @@ export const loginWithSC2 = async (code, isPinCodeOpen) => {
|
||||
scTokens ? scTokens.access_token : '',
|
||||
);
|
||||
account.mutes = await getMutes(account.username);
|
||||
account.ecencyUserData = await getEcencyUser(account.username);
|
||||
|
||||
let jsonMetadata;
|
||||
try {
|
||||
|
@ -174,38 +174,82 @@ export const fetchGlobalProps = async () => {
|
||||
};
|
||||
|
||||
/**
|
||||
* @method getAccount get account data
|
||||
* @param user username
|
||||
* fetches all tranding orders that are not full-filled yet
|
||||
* @param {string} username
|
||||
* @returns {Promise<OpenOrderItem[]>} array of openorders both hive and hbd
|
||||
*/
|
||||
export const getAccount = (user) =>
|
||||
new Promise((resolve, reject) => {
|
||||
try {
|
||||
const account = client.database.getAccounts([user]);
|
||||
resolve(account);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
export const getOpenOrders = async (username) => {
|
||||
try {
|
||||
const rawData = await client.call('condenser_api', 'get_open_orders', [username]);
|
||||
if (!rawData || !rawData.length) {
|
||||
return [];
|
||||
}
|
||||
return rawData;
|
||||
} catch (err) {
|
||||
console.warn('Failed to get open orders', err);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* fetchs all pending converstion requests that are yet to be fullfilled
|
||||
* @param {string} account
|
||||
* @returns {Promise<ConversionRequest[]>} array of conversion requests
|
||||
*/
|
||||
export const getConversionRequests = async (username) => {
|
||||
try {
|
||||
const rawData = await client.database.call('get_conversion_requests', [username]);
|
||||
if (!rawData || !rawData.length) {
|
||||
return [];
|
||||
}
|
||||
return rawData;
|
||||
} catch (err) {
|
||||
console.warn('Failed to get open orders', err);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* fetchs all pending converstion requests that are yet to be fullfilled
|
||||
* @param {string} account
|
||||
* @returns {Promise<SavingsWithdrawRequest[]>} array of requested savings withdraw
|
||||
*/
|
||||
|
||||
export const getSavingsWithdrawFrom = async (username) => {
|
||||
try {
|
||||
const rawData = await client.database.call('get_savings_withdraw_from', [username]);
|
||||
if (!rawData || !rawData.length) {
|
||||
return [];
|
||||
}
|
||||
return rawData;
|
||||
} catch (err) {
|
||||
console.warn('Failed to get open orders', err);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @method getAccount fetch raw account data without post processings
|
||||
* @param username username
|
||||
*/
|
||||
export const getAccount = (username) =>
|
||||
new Promise((resolve, reject) => {
|
||||
client.database
|
||||
.getAccounts([username])
|
||||
.then((response) => {
|
||||
if (response.length) {
|
||||
resolve(response[0]);
|
||||
}
|
||||
throw new Error('Account not found, ' + JSON.stringify(response));
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
|
||||
export const getAccountHistory = (user, type_token) =>
|
||||
export const getAccountHistory = (user, operations) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const op = utils.operationOrders;
|
||||
let wallet_operations_bitmask = utils.makeBitMaskFilter([
|
||||
op.transfer, //HIVE
|
||||
op.author_reward, //HBD, HP
|
||||
op.curation_reward, //HP
|
||||
op.transfer_to_vesting, //HIVE, HP
|
||||
op.withdraw_vesting, //HIVE, HP
|
||||
op.interest, //HP
|
||||
op.transfer_to_savings, //HIVE, HBD
|
||||
op.transfer_from_savings, //HIVE, HBD
|
||||
op.fill_convert_request, //HBD
|
||||
op.fill_order, //HIVE, HBD
|
||||
op.claim_reward_balance, //HP
|
||||
op.sps_fund, //HBD
|
||||
op.comment_benefactor_reward, //HP
|
||||
op.return_vesting_delegation, //HP
|
||||
]);
|
||||
let wallet_operations_bitmask = utils.makeBitMaskFilter(operations);
|
||||
try {
|
||||
const ah = client.call('condenser_api', 'get_account_history', [
|
||||
user,
|
||||
|
113
src/providers/hive/hive.types.ts
Normal file
113
src/providers/hive/hive.types.ts
Normal file
@ -0,0 +1,113 @@
|
||||
|
||||
export interface Vote {
|
||||
percent: number;
|
||||
reputation: number;
|
||||
rshares: string;
|
||||
time: string;
|
||||
timestamp?: number;
|
||||
voter: string;
|
||||
weight: number;
|
||||
reward?: number;
|
||||
}
|
||||
|
||||
export interface DynamicGlobalProperties {
|
||||
hbd_print_rate: number;
|
||||
total_vesting_fund_hive: string;
|
||||
total_vesting_shares: string;
|
||||
hbd_interest_rate: number;
|
||||
head_block_number: number;
|
||||
vesting_reward_percent: number;
|
||||
virtual_supply: string;
|
||||
}
|
||||
|
||||
export interface FeedHistory {
|
||||
current_median_history: {
|
||||
base: string;
|
||||
quote: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface RewardFund {
|
||||
recent_claims: string;
|
||||
reward_balance: string;
|
||||
}
|
||||
|
||||
export interface DelegatedVestingShare {
|
||||
id: number;
|
||||
delegatee: string;
|
||||
delegator: string;
|
||||
min_delegation_time: string;
|
||||
vesting_shares: string;
|
||||
}
|
||||
|
||||
export interface Follow {
|
||||
follower: string;
|
||||
following: string;
|
||||
what: string[];
|
||||
}
|
||||
|
||||
export interface MarketStatistics {
|
||||
hbd_volume: string;
|
||||
highest_bid: string;
|
||||
hive_volume: string;
|
||||
latest: string;
|
||||
lowest_ask: string;
|
||||
percent_change: string;
|
||||
}
|
||||
|
||||
export interface OpenOrderItem {
|
||||
id: number,
|
||||
created: string,
|
||||
expiration: string,
|
||||
seller: string,
|
||||
orderid: number,
|
||||
for_sale: number,
|
||||
sell_price: {
|
||||
base: string,
|
||||
quote: string
|
||||
},
|
||||
real_price: string,
|
||||
rewarded: boolean
|
||||
}
|
||||
|
||||
|
||||
export interface OrdersDataItem {
|
||||
created: string;
|
||||
hbd: number;
|
||||
hive: number;
|
||||
order_price: {
|
||||
base: string;
|
||||
quote: string;
|
||||
}
|
||||
real_price: string;
|
||||
}
|
||||
|
||||
export interface TradeDataItem {
|
||||
current_pays: string;
|
||||
date: number;
|
||||
open_pays: string;
|
||||
}
|
||||
|
||||
export interface OrdersData {
|
||||
bids: OrdersDataItem[];
|
||||
asks: OrdersDataItem[];
|
||||
trading: OrdersDataItem[];
|
||||
}
|
||||
|
||||
export interface ConversionRequest {
|
||||
amount: string;
|
||||
conversion_date: string;
|
||||
id: number;
|
||||
owner: string;
|
||||
requestid: number;
|
||||
}
|
||||
|
||||
export interface SavingsWithdrawRequest {
|
||||
id: number;
|
||||
from: string;
|
||||
to: string;
|
||||
memo: string;
|
||||
request_id: number;
|
||||
amount: string;
|
||||
complete: string;
|
||||
}
|
@ -163,15 +163,14 @@ export const isDefaultFooter = (payload) => ({
|
||||
/**
|
||||
* MW
|
||||
*/
|
||||
export const setCurrency = (currency) => (dispatch) => {
|
||||
export const setCurrency = (currency) => async (dispatch) => {
|
||||
const currencySymbol = getSymbolFromCurrency(currency);
|
||||
|
||||
getCurrencyRate(currency).then((currencyRate) =>
|
||||
dispatch({
|
||||
type: SET_CURRENCY,
|
||||
payload: { currency, currencyRate, currencySymbol },
|
||||
}),
|
||||
);
|
||||
const currencyRate = await getCurrencyRate(currency)
|
||||
dispatch({
|
||||
type: SET_CURRENCY,
|
||||
payload: { currency, currencyRate, currencySymbol },
|
||||
})
|
||||
};
|
||||
|
||||
export const setPinCode = (data) => ({
|
||||
|
82
src/redux/actions/walletActions.ts
Normal file
82
src/redux/actions/walletActions.ts
Normal file
@ -0,0 +1,82 @@
|
||||
import { getLatestQuotes } from '../../providers/ecency/ecency';
|
||||
import { fetchCoinsData } from '../../utils/wallet';
|
||||
import { SET_SELECTED_COINS, SET_PRICE_HISTORY, SET_COINS_DATA, SET_COIN_ACTIVITIES, SET_COIN_QUOTES, RESET_WALLET_DATA } from '../constants/constants';
|
||||
import { CoinActivitiesCollection, CoinBase, CoinData } from '../reducers/walletReducer';
|
||||
import { AppDispatch, RootState } from '../store/store';
|
||||
|
||||
export const setSelectedCoins = (coins: CoinBase[]) => ({
|
||||
payload: coins,
|
||||
type: SET_SELECTED_COINS,
|
||||
});
|
||||
|
||||
|
||||
|
||||
export const setCoinsData = (data: { [key: string]: CoinData }, vsCurrency: string, username: string) => ({
|
||||
payload: {
|
||||
data,
|
||||
vsCurrency,
|
||||
username,
|
||||
},
|
||||
type: SET_COINS_DATA
|
||||
})
|
||||
|
||||
export const setPriceHistory = (coinId: string, vsCurrency: string, data: number[]) => ({
|
||||
payload: {
|
||||
id: coinId,
|
||||
vsCurrency,
|
||||
data
|
||||
},
|
||||
type: SET_PRICE_HISTORY
|
||||
})
|
||||
|
||||
export const setCoinActivities = (coinId: string, data: CoinActivitiesCollection) => ({
|
||||
payload: {
|
||||
id: coinId,
|
||||
data,
|
||||
},
|
||||
type: SET_COIN_ACTIVITIES
|
||||
})
|
||||
|
||||
export const resetWalletData = () => ({
|
||||
type: RESET_WALLET_DATA
|
||||
})
|
||||
|
||||
|
||||
export const fetchCoinQuotes = () => (dispatch, getState) => {
|
||||
const currency = getState().application.currency;
|
||||
console.log("fetching quotes for currency", currency)
|
||||
getLatestQuotes(currency.currencyRate)
|
||||
.then((quotes) => {
|
||||
console.log("Fetched quotes", quotes)
|
||||
dispatch({
|
||||
type: SET_COIN_QUOTES,
|
||||
payload: { ...quotes },
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const fetchAndSetCoinsData = (refresh: boolean = false) => async (dispatch: AppDispatch, getState: RootState) => {
|
||||
const coins = getState().wallet.selectedCoins;
|
||||
const quotes = getState().wallet.quotes;
|
||||
const currentAccount = getState().account.currentAccount;
|
||||
const currency = getState().application.currency;
|
||||
const globalProps = getState().account.globalProps;
|
||||
|
||||
const coinsData = await fetchCoinsData({
|
||||
coins,
|
||||
currentAccount,
|
||||
vsCurrency: currency.currency,
|
||||
currencyRate: currency.currencyRate,
|
||||
globalProps,
|
||||
quotes,
|
||||
refresh
|
||||
})
|
||||
|
||||
return dispatch(setCoinsData(
|
||||
coinsData,
|
||||
currency.currency,
|
||||
currentAccount.username
|
||||
))
|
||||
}
|
@ -113,3 +113,11 @@ export const DELETE_COMMENT_CACHE_ENTRY = 'DELETE_COMMENT_CACHE_ENTRY';
|
||||
|
||||
// TOOLTIPS
|
||||
export const REGISTER_TOOLTIP = 'REGISTER_TOOLTIP';
|
||||
|
||||
//WALLET
|
||||
export const SET_SELECTED_COINS = 'SET_SELECTED_COINS';
|
||||
export const SET_COINS_DATA = 'SET_COIN_DATA';
|
||||
export const SET_PRICE_HISTORY = 'SET_PRICE_HISTORY';
|
||||
export const SET_COIN_ACTIVITIES = 'SET_COIN_ACTIVITIES';
|
||||
export const SET_COIN_QUOTES = 'SET_COIN_QUOTES';
|
||||
export const RESET_WALLET_DATA = 'RESET_WALLET_DATA';
|
||||
|
@ -10,6 +10,7 @@ import customTabsReducer from './customTabsReducer';
|
||||
import editorReducer from './editorReducer';
|
||||
import cacheReducer from './cacheReducer';
|
||||
import walkthroughReducer from './walkthroughReducer';
|
||||
import walletReducer from './walletReducer';
|
||||
|
||||
export default combineReducers({
|
||||
account: accountReducer,
|
||||
@ -23,4 +24,5 @@ export default combineReducers({
|
||||
user,
|
||||
cache: cacheReducer,
|
||||
walkthrough: walkthroughReducer,
|
||||
wallet: walletReducer,
|
||||
});
|
||||
|
138
src/redux/reducers/walletReducer.ts
Normal file
138
src/redux/reducers/walletReducer.ts
Normal file
@ -0,0 +1,138 @@
|
||||
import DEFAULT_COINS from "../../constants/defaultCoins";
|
||||
import { SET_PRICE_HISTORY, SET_SELECTED_COINS, SET_COINS_DATA, SET_COIN_ACTIVITIES, SET_COIN_QUOTES, RESET_WALLET_DATA } from "../constants/constants";
|
||||
|
||||
export interface DataPair {
|
||||
value:string|number;
|
||||
labelId:string;
|
||||
}
|
||||
|
||||
export interface CoinBase {
|
||||
id:string,
|
||||
symbol:string,
|
||||
notCrypto:boolean,
|
||||
}
|
||||
|
||||
export interface CoinData {
|
||||
currentPrice:number;
|
||||
balance:number;
|
||||
savings?:number;
|
||||
unclaimedBalance:string,
|
||||
estimateValue?:number;
|
||||
vsCurrency:string;
|
||||
actions:string[];
|
||||
extraDataPairs?:DataPair[];
|
||||
}
|
||||
|
||||
export interface PriceHistory {
|
||||
expiresAt:number;
|
||||
vsCurrency:string;
|
||||
data:number[];
|
||||
}
|
||||
|
||||
export interface CoinActivity {
|
||||
iconType: string;
|
||||
textKey: string;
|
||||
created: string;
|
||||
expires: string;
|
||||
icon: string;
|
||||
value:string;
|
||||
details: string;
|
||||
memo: string;
|
||||
}
|
||||
|
||||
export interface QuoteItem {
|
||||
lastUpdated:string;
|
||||
percentChange:number;
|
||||
price:number;
|
||||
}
|
||||
|
||||
export interface CoinActivitiesCollection {
|
||||
completed:CoinActivity[],
|
||||
pending:CoinActivity[],
|
||||
}
|
||||
|
||||
|
||||
interface State {
|
||||
selectedCoins:CoinBase[];
|
||||
coinsData:{
|
||||
[key: string]: CoinData;
|
||||
},
|
||||
priceHistories:{
|
||||
[key: string]: PriceHistory;
|
||||
}
|
||||
coinsActivities:{
|
||||
[key: string]:CoinActivitiesCollection;
|
||||
},
|
||||
quotes:{
|
||||
[key: string]: QuoteItem;
|
||||
}
|
||||
vsCurrency:string,
|
||||
username:string,
|
||||
updateTimestamp:number;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const initialState:State = {
|
||||
selectedCoins:DEFAULT_COINS,
|
||||
coinsData:{},
|
||||
priceHistories:{},
|
||||
coinsActivities:{},
|
||||
quotes: null,
|
||||
vsCurrency:'',
|
||||
username:'',
|
||||
updateTimestamp:0
|
||||
};
|
||||
|
||||
export default function (state = initialState, action) {
|
||||
const {type, payload} = action;
|
||||
switch (type) {
|
||||
case RESET_WALLET_DATA:{
|
||||
return {
|
||||
...initialState,
|
||||
selectedCoins:state.selectedCoins
|
||||
}
|
||||
}
|
||||
case SET_SELECTED_COINS:{
|
||||
return {
|
||||
...state,
|
||||
selectedCoin:payload
|
||||
}
|
||||
}
|
||||
case SET_COINS_DATA:{
|
||||
return {
|
||||
...state,
|
||||
coinsData:payload.data,
|
||||
vsCurrency:payload.vsCurrency,
|
||||
username:payload.username,
|
||||
updateTimestamp:new Date().getTime()
|
||||
}
|
||||
}
|
||||
case SET_PRICE_HISTORY:{
|
||||
state.priceHistories[payload.id] = {
|
||||
expiresAt:new Date().getTime() + ONE_HOUR_MS,
|
||||
vsCurrency:payload.vsCurrency,
|
||||
data:payload.data
|
||||
};
|
||||
return {
|
||||
...state
|
||||
}
|
||||
}
|
||||
case SET_COIN_ACTIVITIES:{
|
||||
state.coinsActivities[payload.id] = payload.data
|
||||
return {
|
||||
...state
|
||||
}
|
||||
}
|
||||
case SET_COIN_QUOTES:{
|
||||
return {
|
||||
...state,
|
||||
quotes:payload,
|
||||
}
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
const ONE_HOUR_MS = 60 * 60 * 1000;
|
@ -27,6 +27,8 @@ const transformWalkthroughMap = createTransform(
|
||||
{whitelist:['walkthrough']}
|
||||
);
|
||||
|
||||
|
||||
|
||||
// Middleware: Redux Persist Config
|
||||
const persistConfig = {
|
||||
// Root
|
||||
@ -36,7 +38,10 @@ const persistConfig = {
|
||||
// Blacklist (Don't Save Specific Reducers)
|
||||
blacklist: ['nav', 'application', 'communities', 'user'],
|
||||
timeout: 0,
|
||||
transforms:[transformCacheVoteMap,transformWalkthroughMap],
|
||||
transforms:[
|
||||
transformCacheVoteMap,
|
||||
transformWalkthroughMap
|
||||
]
|
||||
};
|
||||
|
||||
// Middleware: Redux Persist Persisted Reducer
|
||||
@ -58,4 +63,4 @@ export { store, persistor };
|
||||
// Infer the `RootState` and `AppDispatch` types from the store itself
|
||||
export type RootState = ReturnType<typeof store.getState>
|
||||
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
|
||||
export type AppDispatch = typeof store.dispatch
|
||||
export type AppDispatch = typeof store.dispatch
|
||||
|
@ -16,7 +16,6 @@ import PushNotification from 'react-native-push-notification';
|
||||
import VersionNumber from 'react-native-version-number';
|
||||
import ReceiveSharingIntent from 'react-native-receive-sharing-intent';
|
||||
import Matomo from 'react-native-matomo-sdk';
|
||||
import uniqueId from 'react-native-unique-id';
|
||||
|
||||
// Constants
|
||||
import AUTH_TYPE from '../../../constants/authType';
|
||||
@ -41,6 +40,7 @@ import {
|
||||
setLastUpdateCheck,
|
||||
} from '../../../realm/realm';
|
||||
import { getUser, getPost, getDigitPinCode, getMutes } from '../../../providers/hive/dhive';
|
||||
import { getUser as getEcencyUser } from '../../../providers/ecency/ePoint';
|
||||
import {
|
||||
migrateToMasterKeyWithAccessToken,
|
||||
refreshSCToken,
|
||||
@ -51,6 +51,7 @@ import {
|
||||
markActivityAsRead,
|
||||
markNotifications,
|
||||
getUnreadNotificationCount,
|
||||
getLatestQuotes,
|
||||
} from '../../../providers/ecency/ecency';
|
||||
import { fetchLatestAppVersion } from '../../../providers/github/github';
|
||||
import { navigate } from '../../../navigation/service';
|
||||
@ -95,6 +96,7 @@ import {
|
||||
updateActiveBottomTab,
|
||||
} from '../../../redux/actions/uiAction';
|
||||
import { setFeedPosts, setInitPosts } from '../../../redux/actions/postsAction';
|
||||
import { fetchCoinQuotes } from '../../../redux/actions/walletActions';
|
||||
|
||||
import { encryptKey } from '../../../utils/crypto';
|
||||
|
||||
@ -619,6 +621,7 @@ class ApplicationContainer extends Component {
|
||||
_refreshGlobalProps = () => {
|
||||
const { actions } = this.props;
|
||||
actions.fetchGlobalProperties();
|
||||
actions.fetchCoinQuotes();
|
||||
};
|
||||
|
||||
_getUserDataFromRealm = async () => {
|
||||
@ -759,6 +762,7 @@ class ApplicationContainer extends Component {
|
||||
|
||||
accountData.unread_activity_count = await getUnreadNotificationCount();
|
||||
accountData.mutes = await getMutes(realmObject.username);
|
||||
accountData.ecencyUserData = await getEcencyUser(realmObject.username);
|
||||
dispatch(updateCurrentAccount(accountData));
|
||||
|
||||
this._connectNotificationServer(accountData.name);
|
||||
@ -812,9 +816,7 @@ class ApplicationContainer extends Component {
|
||||
}
|
||||
if (settings.nsfw !== '') dispatch(setNsfw(settings.nsfw));
|
||||
|
||||
if (settings.currency !== '') {
|
||||
dispatch(setCurrency(settings.currency !== '' ? settings.currency : 'usd'));
|
||||
}
|
||||
await dispatch(setCurrency(settings.currency !== '' ? settings.currency : 'usd'));
|
||||
}
|
||||
};
|
||||
|
||||
@ -941,6 +943,7 @@ class ApplicationContainer extends Component {
|
||||
|
||||
_currentAccount.unread_activity_count = await getUnreadNotificationCount();
|
||||
_currentAccount.mutes = await getMutes(_currentAccount.username);
|
||||
_currentAccount.ecencyUserData = await getEcencyUser(_currentAccount.username);
|
||||
dispatch(updateCurrentAccount(_currentAccount));
|
||||
};
|
||||
|
||||
@ -1068,6 +1071,7 @@ export default connect(
|
||||
...bindActionCreators(
|
||||
{
|
||||
fetchGlobalProperties,
|
||||
fetchCoinQuotes,
|
||||
},
|
||||
dispatch,
|
||||
),
|
||||
|
56
src/screens/coinDetails/children/activitiesList.tsx
Normal file
56
src/screens/coinDetails/children/activitiesList.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
import React, { ComponentType, JSXElementConstructor, ReactElement } from 'react'
|
||||
import { useIntl } from 'react-intl';
|
||||
import { SectionList, Text } from 'react-native';
|
||||
import { Transaction } from '../../../components';
|
||||
import { RefreshControl, RefreshControlProps } from '../../../components/atoms';
|
||||
import { CoinActivity } from '../../../redux/reducers/walletReducer';
|
||||
import styles from './children.styles';
|
||||
|
||||
interface ActivitiesListProps {
|
||||
header: ComponentType<any> | ReactElement<any, string | JSXElementConstructor<any>>
|
||||
pendingActivities: CoinActivity[];
|
||||
completedActivities: CoinActivity[];
|
||||
refreshControlProps:RefreshControlProps
|
||||
}
|
||||
|
||||
const ActivitiesList = ({ header, completedActivities, pendingActivities, refreshControlProps }: ActivitiesListProps) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const _renderActivityItem = ({ item, index }) => {
|
||||
return <Transaction item={item} index={index} />
|
||||
}
|
||||
|
||||
const sections = [];
|
||||
|
||||
if (pendingActivities && pendingActivities.length) {
|
||||
sections.push({
|
||||
title: intl.formatMessage({id:'wallet.pending_requests'}),
|
||||
data: pendingActivities
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
sections.push({
|
||||
title: intl.formatMessage({id:'wallet.activities'}),
|
||||
data: completedActivities || []
|
||||
})
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<SectionList
|
||||
style={styles.list}
|
||||
contentContainerStyle={styles.listContent}
|
||||
sections={sections}
|
||||
renderItem={_renderActivityItem}
|
||||
keyExtractor={(item, index) => `activity_item_${index}_${item.created}`}
|
||||
renderSectionHeader={({ section: { title } }) => (
|
||||
<Text style={styles.textActivities}>{title}</Text>
|
||||
)}
|
||||
ListHeaderComponent={header}
|
||||
refreshControl={<RefreshControl {...refreshControlProps}/>}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default ActivitiesList
|
130
src/screens/coinDetails/children/children.styles.ts
Normal file
130
src/screens/coinDetails/children/children.styles.ts
Normal file
@ -0,0 +1,130 @@
|
||||
import { TextStyle, ViewStyle } from 'react-native';
|
||||
import EStyleSheet from 'react-native-extended-stylesheet';
|
||||
|
||||
export const CHART_NEGATIVE_MARGIN = 12
|
||||
export default EStyleSheet.create({
|
||||
|
||||
card: {
|
||||
marginVertical:8,
|
||||
borderRadius:12,
|
||||
overflow: 'hidden',
|
||||
backgroundColor: '$primaryLightBackground'
|
||||
} as ViewStyle,
|
||||
basicsContainer:{
|
||||
alignItems:'center',
|
||||
padding:16
|
||||
} as ViewStyle,
|
||||
coinTitleContainer:{
|
||||
flexDirection:'row',
|
||||
marginTop:8
|
||||
} as ViewStyle,
|
||||
textCoinTitle:{
|
||||
color: '$primaryBlack',
|
||||
fontSize: 34,
|
||||
fontWeight:'700',
|
||||
} as TextStyle,
|
||||
textHeaderChange:{
|
||||
color: '$primaryDarkText',
|
||||
fontSize: 16,
|
||||
marginBottom:32,
|
||||
} as TextStyle,
|
||||
|
||||
textPositive:{
|
||||
color: '$primaryGreen'
|
||||
} as TextStyle,
|
||||
textNegative:{
|
||||
color: '$primaryRed'
|
||||
} as TextStyle,
|
||||
textBasicValue:{
|
||||
color: '$primaryBlack',
|
||||
fontWeight:'700',
|
||||
fontSize: 28,
|
||||
|
||||
} as TextStyle,
|
||||
textBasicLabel:{
|
||||
color: '$primaryDarkText',
|
||||
fontSize: 14,
|
||||
marginBottom:16,
|
||||
} as TextStyle,
|
||||
|
||||
extraDataContainer:{
|
||||
flexDirection:'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems:'center',
|
||||
width:'100%',
|
||||
marginVertical: 2,
|
||||
} as ViewStyle,
|
||||
|
||||
textExtraValue:{
|
||||
color: '$primaryDarkText',
|
||||
fontWeight:'700',
|
||||
fontSize: 18,
|
||||
|
||||
} as TextStyle,
|
||||
textExtraLabel:{
|
||||
color: '$primaryDarkText',
|
||||
fontSize: 14,
|
||||
} as TextStyle,
|
||||
|
||||
rangeContainer:{
|
||||
flexDirection:'row',
|
||||
alignItems:'center',
|
||||
justifyContent:'space-between',
|
||||
borderRadius:32,
|
||||
} as ViewStyle,
|
||||
|
||||
rangeOptionWrapper:{
|
||||
borderRadius:32,
|
||||
paddingVertical:16,
|
||||
paddingHorizontal:24
|
||||
} as ViewStyle,
|
||||
|
||||
textRange:{
|
||||
fontSize:16,
|
||||
} as TextStyle,
|
||||
|
||||
chartContainer:{
|
||||
height: 168,
|
||||
marginTop: 16,
|
||||
marginLeft:-CHART_NEGATIVE_MARGIN,
|
||||
overflow: 'hidden',
|
||||
} as ViewStyle,
|
||||
list:{
|
||||
flex:1,
|
||||
} as ViewStyle,
|
||||
listContent:{
|
||||
paddingBottom:56,
|
||||
marginHorizontal:16
|
||||
} as ViewStyle ,
|
||||
|
||||
//COIN ACTIONS STYLES
|
||||
actionBtnContainer:{
|
||||
flexGrow:1
|
||||
} as ViewStyle,
|
||||
actionsContainer:{
|
||||
flexDirection:'row',
|
||||
flexWrap:'wrap'
|
||||
} as ViewStyle,
|
||||
actionContainer:{
|
||||
paddingHorizontal:16,
|
||||
marginVertical:8,
|
||||
marginHorizontal:4,
|
||||
backgroundColor:'$primaryLightBackground',
|
||||
height: 40,
|
||||
borderRadius:20,
|
||||
justifyContent:'center',
|
||||
alignItems:'center',
|
||||
} as ViewStyle,
|
||||
actionText:{
|
||||
color: '$primaryBlack'
|
||||
} as TextStyle,
|
||||
|
||||
textActivities:{
|
||||
color:'$primaryBlack',
|
||||
fontWeight:'600',
|
||||
fontSize:18,
|
||||
paddingVertical:16,
|
||||
backgroundColor:'$primaryBackgroundColor'
|
||||
} as TextStyle
|
||||
});
|
||||
|
38
src/screens/coinDetails/children/coinActions.tsx
Normal file
38
src/screens/coinDetails/children/coinActions.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
import React, { Fragment } from 'react'
|
||||
import { useIntl } from 'react-intl'
|
||||
import { View, Text } from 'react-native'
|
||||
import { TouchableOpacity } from 'react-native-gesture-handler'
|
||||
import { withNavigation } from 'react-navigation'
|
||||
import styles from './children.styles'
|
||||
|
||||
interface CoinActionsProps {
|
||||
actions: string[];
|
||||
onActionPress: (action: string) => void;
|
||||
}
|
||||
|
||||
export const CoinActions = withNavigation(({ actions, onActionPress }: CoinActionsProps) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const _renderItem = (item: string, index: number) => {
|
||||
|
||||
const _onPress = () => {
|
||||
onActionPress(item)
|
||||
}
|
||||
|
||||
return (
|
||||
<TouchableOpacity key={`action-${item}-${index}`} style={styles.actionContainer} containerStyle={styles.actionBtnContainer} onPress={_onPress}>
|
||||
<Fragment>
|
||||
<Text style={styles.actionText}>
|
||||
{intl.formatMessage({ id: `wallet.${item}` })}
|
||||
</Text>
|
||||
</Fragment>
|
||||
</TouchableOpacity>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.actionsContainer}>
|
||||
{actions.map(_renderItem)}
|
||||
</View>
|
||||
)
|
||||
});
|
61
src/screens/coinDetails/children/coinBasics.tsx
Normal file
61
src/screens/coinDetails/children/coinBasics.tsx
Normal file
@ -0,0 +1,61 @@
|
||||
import React, { Fragment } from 'react'
|
||||
import { useIntl } from 'react-intl';
|
||||
import { View, Text } from 'react-native'
|
||||
import { DataPair } from '../../../redux/reducers/walletReducer'
|
||||
import styles from './children.styles'
|
||||
|
||||
|
||||
interface CoinBasicsProps {
|
||||
valuePairs: DataPair[];
|
||||
extraData: DataPair[];
|
||||
coinSymbol: string;
|
||||
percentChange: number;
|
||||
}
|
||||
|
||||
export const CoinBasics = ({ valuePairs, extraData, coinSymbol, percentChange }: CoinBasicsProps) => {
|
||||
const intl = useIntl();
|
||||
const _renderCoinHeader = (
|
||||
<>
|
||||
<View style={styles.coinTitleContainer}>
|
||||
<Text style={styles.textCoinTitle}>{coinSymbol}</Text>
|
||||
</View>
|
||||
<Text
|
||||
style={styles.textHeaderChange}>
|
||||
{intl.formatMessage({ id: 'wallet.change' })}
|
||||
<Text
|
||||
style={percentChange > 0 ? styles.textPositive : styles.textNegative}>
|
||||
{` ${percentChange >= 0 ? '+' : ''}${percentChange.toFixed(1)}%`}
|
||||
</Text>
|
||||
|
||||
</Text>
|
||||
</>
|
||||
)
|
||||
|
||||
const _renderValuePair = (args: DataPair, index: number) => {
|
||||
const label = intl.formatMessage({ id: `wallet.${args.labelId}` })
|
||||
return (
|
||||
<Fragment key={`basic-data-${args.labelId}-${index}`}>
|
||||
<Text style={styles.textBasicValue}>{args.value}</Text>
|
||||
<Text style={styles.textBasicLabel}>{label}</Text>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
const _renderExtraData = (args: DataPair, index: number) => {
|
||||
const label = intl.formatMessage({ id: `wallet.${args.labelId}` })
|
||||
return (
|
||||
<View key={`extra-data-${args.labelId}-${index}`} style={styles.extraDataContainer}>
|
||||
<Text style={styles.textExtraLabel}>{label}</Text>
|
||||
<Text style={styles.textExtraValue}>{args.value}</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={[styles.card, styles.basicsContainer]}>
|
||||
{_renderCoinHeader}
|
||||
{valuePairs.map(_renderValuePair)}
|
||||
{extraData && extraData.map(_renderExtraData)}
|
||||
</View>
|
||||
)
|
||||
}
|
53
src/screens/coinDetails/children/coinChart.tsx
Normal file
53
src/screens/coinDetails/children/coinChart.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { View, Dimensions } from 'react-native'
|
||||
import { RangeSelector } from '.';
|
||||
import { SimpleChart } from '../../../components'
|
||||
import { useAppSelector } from '../../../hooks';
|
||||
import { fetchMarketChart } from '../../../providers/coingecko/coingecko';
|
||||
import styles, { CHART_NEGATIVE_MARGIN } from './children.styles';
|
||||
|
||||
|
||||
interface CoinChartProps {
|
||||
coinId:string;
|
||||
}
|
||||
|
||||
export const CoinChart = ({coinId}:CoinChartProps) => {
|
||||
|
||||
const priceHistory = useAppSelector(state=>state.wallet.priceHistories[coinId]);
|
||||
|
||||
const [range, setRange] = useState(1);
|
||||
const [chartData, setChartData] = useState(priceHistory.data);
|
||||
|
||||
const _fetchMarketData = async (days:number) => {
|
||||
const marketData = await fetchMarketChart(coinId, 'usd', days, 'hourly')
|
||||
setChartData(marketData.prices.map(item=>item.yValue));
|
||||
}
|
||||
|
||||
|
||||
const _onRangeChange = (range) => {
|
||||
setRange(range);
|
||||
_fetchMarketData(range);
|
||||
}
|
||||
|
||||
const _renderGraph = () => {
|
||||
const _baseWidth = Dimensions.get("window").width - 32 + CHART_NEGATIVE_MARGIN;
|
||||
return (
|
||||
<View style={styles.chartContainer}>
|
||||
<SimpleChart
|
||||
data={chartData}
|
||||
baseWidth={_baseWidth} // from react-native
|
||||
chartHeight={200}
|
||||
showLine={true}
|
||||
showLabels={true}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
return (
|
||||
<>
|
||||
<View style={styles.card}>
|
||||
{_renderGraph()}
|
||||
</View>
|
||||
<RangeSelector range={range} onRangeChange={_onRangeChange} />
|
||||
</>
|
||||
)
|
||||
}
|
66
src/screens/coinDetails/children/coinSummary.tsx
Normal file
66
src/screens/coinDetails/children/coinSummary.tsx
Normal file
@ -0,0 +1,66 @@
|
||||
import React from 'react'
|
||||
import { View } from 'react-native'
|
||||
import { CoinActions, CoinBasics, CoinChart } from '.'
|
||||
import { FormattedCurrency } from '../../../components'
|
||||
import { COIN_IDS } from '../../../constants/defaultCoins'
|
||||
import { CoinData, DataPair } from '../../../redux/reducers/walletReducer'
|
||||
|
||||
export interface CoinSummaryProps {
|
||||
id:string;
|
||||
coinSymbol:string;
|
||||
coinData:CoinData;
|
||||
percentChagne:number;
|
||||
onActionPress:(action:string)=>void;
|
||||
}
|
||||
|
||||
export const CoinSummary = ({
|
||||
coinSymbol,
|
||||
id,
|
||||
coinData,
|
||||
percentChagne,
|
||||
onActionPress,
|
||||
}:CoinSummaryProps) => {
|
||||
const {
|
||||
balance,
|
||||
estimateValue,
|
||||
savings,
|
||||
extraDataPairs,
|
||||
actions
|
||||
} = coinData
|
||||
|
||||
const valuePairs = [
|
||||
{
|
||||
labelId:'amount_desc',
|
||||
value:balance
|
||||
}
|
||||
] as DataPair[]
|
||||
|
||||
if(estimateValue !== undefined){
|
||||
valuePairs.push({
|
||||
labelId:'estimated_value',
|
||||
value:<FormattedCurrency isApproximate isToken value={estimateValue} />,
|
||||
})
|
||||
}
|
||||
|
||||
if(savings !== undefined){
|
||||
valuePairs.push({
|
||||
labelId:'savings',
|
||||
value:savings
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<View>
|
||||
<CoinBasics
|
||||
valuePairs={valuePairs}
|
||||
extraData={extraDataPairs}
|
||||
coinSymbol={coinSymbol}
|
||||
percentChange={percentChagne}
|
||||
/>
|
||||
<CoinActions actions={actions} onActionPress={onActionPress}/>
|
||||
{
|
||||
id !== COIN_IDS.ECENCY && id !== COIN_IDS.HP && <CoinChart coinId={id} />
|
||||
}
|
||||
</View>
|
||||
)
|
||||
}
|
6
src/screens/coinDetails/children/index.ts
Normal file
6
src/screens/coinDetails/children/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export * from './coinBasics';
|
||||
export * from './coinChart';
|
||||
export * from './rangeSelector';
|
||||
export * from './coinSummary';
|
||||
export * from './activitiesList';
|
||||
export * from './coinActions';
|
75
src/screens/coinDetails/children/rangeSelector.tsx
Normal file
75
src/screens/coinDetails/children/rangeSelector.tsx
Normal file
@ -0,0 +1,75 @@
|
||||
import React, { useState } from 'react'
|
||||
import { View, Text } from 'react-native'
|
||||
import EStyleSheet from 'react-native-extended-stylesheet'
|
||||
import { TouchableOpacity } from 'react-native-gesture-handler'
|
||||
import styles from './children.styles'
|
||||
|
||||
interface RangeOption {
|
||||
label:string;
|
||||
value:number;
|
||||
}
|
||||
|
||||
interface RangeSelectorProps {
|
||||
range:number;
|
||||
onRangeChange:(range:number)=>void;
|
||||
}
|
||||
|
||||
export const RangeSelector = ({range, onRangeChange}:RangeSelectorProps) => {
|
||||
|
||||
const _onSelection = (range:number) => {
|
||||
console.log('selection', range)
|
||||
onRangeChange(range);
|
||||
//TODO: implement on range change prop
|
||||
}
|
||||
|
||||
const _renderRangeButtons = FILTERS.map((item:RangeOption)=>(
|
||||
<TouchableOpacity key={`range option-${item.value}`} onPress={()=>_onSelection(item.value)} >
|
||||
<View style={{
|
||||
...styles.rangeOptionWrapper,
|
||||
backgroundColor: EStyleSheet.value(
|
||||
item.value === range ?
|
||||
'$primaryDarkText':'$primaryLightBackground'
|
||||
)
|
||||
}}>
|
||||
<Text style={{
|
||||
...styles.textRange,
|
||||
color: EStyleSheet.value(
|
||||
item.value === range ?
|
||||
'$white':'$primaryDarkText'
|
||||
)
|
||||
}}>
|
||||
{item.label}
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
))
|
||||
|
||||
return (
|
||||
<View style={[styles.card, styles.rangeContainer]}>
|
||||
{_renderRangeButtons}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const FILTERS = [
|
||||
{
|
||||
label:'24H',
|
||||
value:1
|
||||
},
|
||||
{
|
||||
label:'1W',
|
||||
value:7
|
||||
},
|
||||
{
|
||||
label:'1M',
|
||||
value:20
|
||||
},
|
||||
{
|
||||
label:'1Y',
|
||||
value:365
|
||||
},
|
||||
{
|
||||
label:'5Y',
|
||||
value:365*5
|
||||
},
|
||||
] as RangeOption[]
|
3
src/screens/coinDetails/index.ts
Normal file
3
src/screens/coinDetails/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import CoinDetails from './screen/coinDetailsScreen';
|
||||
|
||||
export default CoinDetails;
|
160
src/screens/coinDetails/screen/coinDetailsScreen.tsx
Normal file
160
src/screens/coinDetails/screen/coinDetailsScreen.tsx
Normal file
@ -0,0 +1,160 @@
|
||||
import { View, Text, Alert, AppState, AppStateStatus } from 'react-native'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { BasicHeader, Transaction } from '../../../components'
|
||||
import { CoinSummary } from '../children'
|
||||
import styles from './screen.styles';
|
||||
import ActivitiesList from '../children/activitiesList'
|
||||
import { withNavigation } from 'react-navigation'
|
||||
import { useAppDispatch, useAppSelector } from '../../../hooks'
|
||||
import { CoinActivitiesCollection, CoinActivity, CoinData, QuoteItem } from '../../../redux/reducers/walletReducer';
|
||||
import { fetchCoinActivities } from '../../../utils/wallet';
|
||||
import { fetchAndSetCoinsData, setCoinActivities } from '../../../redux/actions/walletActions';
|
||||
import { openPinCodeModal } from '../../../redux/actions/applicationActions';
|
||||
import { navigate } from '../../../navigation/service';
|
||||
import ROUTES from '../../../constants/routeNames';
|
||||
import { COIN_IDS } from '../../../constants/defaultCoins';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
export interface CoinDetailsScreenParams {
|
||||
coinId:string;
|
||||
}
|
||||
|
||||
interface CoinDetailsScreenProps {
|
||||
navigation:any
|
||||
}
|
||||
|
||||
const CoinDetailsScreen = ({navigation}:CoinDetailsScreenProps) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const coinId = navigation.getParam('coinId');
|
||||
if(!coinId){
|
||||
throw new Error("Coin symbol must be passed")
|
||||
}
|
||||
|
||||
//refs
|
||||
const appState = useRef(AppState.currentState);
|
||||
|
||||
//redux props
|
||||
const currentAccount = useAppSelector(state=>state.account.currentAccount);
|
||||
const globalProps = useAppSelector(state=>state.account.globalProps);
|
||||
const selectedCoins = useAppSelector(state=>state.wallet.selectedCoins);
|
||||
const coinData:CoinData = useAppSelector(state=>state.wallet.coinsData[coinId]);
|
||||
const quote:QuoteItem = useAppSelector(state=>state.wallet.quotes[coinId]);
|
||||
const coinActivities:CoinActivitiesCollection = useAppSelector(state=>state.wallet.coinsActivities[coinId]);
|
||||
const isPinCodeOpen = useAppSelector(state=>state.application.isPinCodeOpen);
|
||||
|
||||
//state
|
||||
const [symbol] = useState(selectedCoins.find((item)=>item.id===coinId).symbol);
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
|
||||
//side-effects
|
||||
useEffect(()=>{
|
||||
_fetchDetails();
|
||||
AppState.addEventListener('change', _handleAppStateChange);
|
||||
return _cleanup;
|
||||
}, [])
|
||||
|
||||
|
||||
const _cleanup = () => {
|
||||
AppState.removeEventListener('change', _handleAppStateChange);
|
||||
}
|
||||
|
||||
|
||||
const _handleAppStateChange = (nextAppState:AppStateStatus) => {
|
||||
if (appState.current.match(/inactive|background/) && nextAppState === 'active') {
|
||||
console.log("updating coins activities on app resume", coinId)
|
||||
_fetchDetails();
|
||||
}
|
||||
|
||||
appState.current = nextAppState;
|
||||
}
|
||||
|
||||
|
||||
const _fetchDetails = async (refresh = false) => {
|
||||
|
||||
if(refresh){
|
||||
setRefreshing(refresh);
|
||||
dispatch(fetchAndSetCoinsData(refresh));
|
||||
}
|
||||
|
||||
const _activites = await fetchCoinActivities(currentAccount.name, coinId, symbol, globalProps);
|
||||
dispatch(setCoinActivities(coinId, _activites));
|
||||
setRefreshing(false);
|
||||
}
|
||||
|
||||
|
||||
if(!coinData){
|
||||
Alert.alert("Invalid coin data");
|
||||
navigation.goBack();
|
||||
}
|
||||
|
||||
|
||||
const _onActionPress = (transferType:string) => {
|
||||
|
||||
let navigateTo = ROUTES.SCREENS.TRANSFER
|
||||
let navigateParams = {};
|
||||
|
||||
if(coinId === COIN_IDS.ECENCY && transferType !== 'dropdown_transfer'){
|
||||
navigateTo = ROUTES.SCREENS.REDEEM;
|
||||
navigateParams = {
|
||||
balance:coinData.balance,
|
||||
redeemType:transferType === 'dropdown_promote'?'promote':'boost',
|
||||
}
|
||||
} else {
|
||||
const balance = transferType === 'withdraw_hive' || transferType === 'withdraw_hbd'
|
||||
? coinData.savings : coinData.balance;
|
||||
navigateParams = {
|
||||
transferType:coinId === COIN_IDS.ECENCY?'points':transferType,
|
||||
fundType:coinId === COIN_IDS.ECENCY?'ESTM':symbol,
|
||||
balance
|
||||
};
|
||||
}
|
||||
|
||||
if (isPinCodeOpen) {
|
||||
dispatch(
|
||||
openPinCodeModal({
|
||||
navigateTo,
|
||||
navigateParams,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
navigate({
|
||||
routeName: navigateTo,
|
||||
params: navigateParams
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const _onRefresh = () => {
|
||||
_fetchDetails(true);
|
||||
}
|
||||
|
||||
|
||||
const _renderHeaderComponent = (
|
||||
<CoinSummary
|
||||
id={coinId}
|
||||
coinSymbol={symbol}
|
||||
coinData={coinData}
|
||||
percentChagne={quote.percentChange || 0}
|
||||
onActionPress={_onActionPress} />
|
||||
)
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<BasicHeader title={intl.formatMessage({id:'wallet.coin_details'})} />
|
||||
<ActivitiesList
|
||||
header={_renderHeaderComponent}
|
||||
completedActivities={coinActivities?.completed || []}
|
||||
pendingActivities={coinActivities?.pending || []}
|
||||
refreshControlProps={{
|
||||
refreshing,
|
||||
onRefresh:_onRefresh
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export default withNavigation(CoinDetailsScreen)
|
10
src/screens/coinDetails/screen/screen.styles.ts
Normal file
10
src/screens/coinDetails/screen/screen.styles.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import EStyleSheet from 'react-native-extended-stylesheet';
|
||||
|
||||
export default EStyleSheet.create({
|
||||
container: {
|
||||
flex:1,
|
||||
backgroundColor: '$primaryBackgroundColor',
|
||||
},
|
||||
|
||||
|
||||
});
|
@ -27,6 +27,7 @@ import TagResult from './tagResult';
|
||||
import { Community } from './community';
|
||||
import Communities from './communities';
|
||||
import ReferScreen from './referScreen/referScreen';
|
||||
import CoinDetails from './coinDetails';
|
||||
|
||||
export {
|
||||
Bookmarks,
|
||||
@ -58,4 +59,5 @@ export {
|
||||
Communities,
|
||||
WebBrowser,
|
||||
ReferScreen,
|
||||
CoinDetails,
|
||||
};
|
||||
|
@ -36,6 +36,7 @@ import {
|
||||
} from '../../../realm/realm';
|
||||
import { updateCurrentAccount, removeOtherAccount } from '../../../redux/actions/accountAction';
|
||||
import { getDigitPinCode, getMutes, getUser } from '../../../providers/hive/dhive';
|
||||
import { getUser as getEcencyUser } from '../../../providers/ecency/ePoint';
|
||||
|
||||
// Utils
|
||||
import { encryptKey, decryptKey } from '../../../utils/crypto';
|
||||
@ -319,6 +320,7 @@ class PinCodeContainer extends Component {
|
||||
//get unread notifications
|
||||
_currentAccount.unread_activity_count = await getUnreadNotificationCount();
|
||||
_currentAccount.mutes = await getMutes(_currentAccount.username);
|
||||
_currentAccount.ecencyUserData = await getEcencyUser(_currentAccount.username);
|
||||
dispatch(updateCurrentAccount({ ..._currentAccount }));
|
||||
dispatch(closePinCodeModal());
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ export default EStyleSheet.create({
|
||||
borderWidth: 1,
|
||||
borderColor: '$borderColor',
|
||||
borderRadius: 8,
|
||||
paddingHorizontal: 10,
|
||||
paddingHorizontal: 12,
|
||||
color: '$primaryBlack',
|
||||
width: 172,
|
||||
minHeight: 35,
|
||||
@ -102,7 +102,7 @@ export default EStyleSheet.create({
|
||||
},
|
||||
dropdownText: {
|
||||
fontSize: 14,
|
||||
paddingLeft: 16,
|
||||
paddingLeft: 12,
|
||||
paddingHorizontal: 14,
|
||||
color: '$primaryDarkGray',
|
||||
},
|
||||
@ -118,7 +118,6 @@ export default EStyleSheet.create({
|
||||
height: 44,
|
||||
width: '100%',
|
||||
borderRadius: 8,
|
||||
marginHorizontal: 2,
|
||||
},
|
||||
dropdown: {
|
||||
flexGrow: 1,
|
||||
|
125
src/screens/wallet/children/children.styles.ts
Normal file
125
src/screens/wallet/children/children.styles.ts
Normal file
@ -0,0 +1,125 @@
|
||||
import { TextStyle, ViewStyle } from 'react-native';
|
||||
import EStyleSheet from 'react-native-extended-stylesheet';
|
||||
import { ImageStyle } from 'react-native-fast-image';
|
||||
|
||||
export default EStyleSheet.create({
|
||||
cardContainer: {
|
||||
backgroundColor:'$primaryLightBackground',
|
||||
marginVertical: 8,
|
||||
marginHorizontal: 16,
|
||||
borderRadius: 12,
|
||||
overflow: 'hidden',
|
||||
borderWidth: 2,
|
||||
borderColor: "$primaryLightBackground"
|
||||
} as ViewStyle,
|
||||
|
||||
cardHeader:{
|
||||
flexDirection:'row',
|
||||
alignItems:'center',
|
||||
paddingHorizontal: 16,
|
||||
paddingTop:16,
|
||||
} as ViewStyle,
|
||||
|
||||
cardTitleContainer:{
|
||||
marginHorizontal: 8,
|
||||
flex:1,
|
||||
} as ViewStyle,
|
||||
|
||||
cardValuesContainer:{
|
||||
marginHorizontal: 8,
|
||||
|
||||
justifyContent:'flex-end'
|
||||
} as ViewStyle,
|
||||
|
||||
claimContainer:{
|
||||
flexDirection:'row',
|
||||
justifyContent:'center',
|
||||
alignItems:'center',
|
||||
marginTop:8
|
||||
} as ViewStyle,
|
||||
|
||||
claimBtn:{
|
||||
flexDirection: 'row',
|
||||
paddingHorizontal: 16
|
||||
} as ViewStyle,
|
||||
|
||||
claimBtnTitle:{
|
||||
color: '$pureWhite',
|
||||
fontSize: 14,
|
||||
fontWeight: 'bold',
|
||||
alignSelf: 'center',
|
||||
} as ViewStyle,
|
||||
|
||||
claimIconWrapper: {
|
||||
backgroundColor: '$pureWhite',
|
||||
justifyContent: 'center',
|
||||
alignSelf: 'center',
|
||||
alignItems: 'center',
|
||||
borderRadius: 20,
|
||||
marginLeft: 20,
|
||||
width: 24,
|
||||
height: 24,
|
||||
} as ViewStyle,
|
||||
|
||||
logo:{
|
||||
height:24,
|
||||
width:24,
|
||||
borderRadius:12,
|
||||
backgroundColor:'$primaryBlue',
|
||||
} as ImageStyle,
|
||||
|
||||
menuIcon:{
|
||||
color: '$primaryBlue',
|
||||
paddingLeft:12,
|
||||
} as ViewStyle,
|
||||
|
||||
header: {
|
||||
backgroundColor: '$primaryBackgroundColor',
|
||||
},
|
||||
dotStyle: {
|
||||
backgroundColor: '$primaryDarkText',
|
||||
},
|
||||
chartContainer:{
|
||||
height:112
|
||||
},
|
||||
cardFooter:{
|
||||
position:'absolute',
|
||||
bottom:8,
|
||||
left:76,
|
||||
right:16,
|
||||
paddingTop:8,
|
||||
flexDirection:'row',
|
||||
justifyContent:'space-between',
|
||||
borderColor:'$chartText',
|
||||
borderTopWidth:EStyleSheet.hairlineWidth,
|
||||
} as ViewStyle,
|
||||
textDiffPositive:{
|
||||
fontSize:18,
|
||||
color: '$primaryGreen'
|
||||
} as TextStyle,
|
||||
textDiffNegative:{
|
||||
fontSize:16,
|
||||
color: '$primaryRed'
|
||||
} as TextStyle,
|
||||
textCurValue:{
|
||||
fontSize:16,
|
||||
color: '$primaryBlack',
|
||||
fontWeight: '300',
|
||||
} as TextStyle,
|
||||
textTitle:{
|
||||
fontSize:16,
|
||||
color: '$primaryBlack',
|
||||
fontWeight: '500'
|
||||
},
|
||||
textSubtitle:{
|
||||
fontSize:14,
|
||||
color: '$primaryDarkText',
|
||||
fontWeight: '300',
|
||||
} as TextStyle,
|
||||
textSubtitleRight:{
|
||||
fontSize:14,
|
||||
color: '$primaryDarkText',
|
||||
fontWeight: '300',
|
||||
textAlign:'right'
|
||||
} as TextStyle
|
||||
});
|
146
src/screens/wallet/children/coinCard.tsx
Normal file
146
src/screens/wallet/children/coinCard.tsx
Normal file
@ -0,0 +1,146 @@
|
||||
import { View, Text, Dimensions, TouchableOpacity } from 'react-native';
|
||||
import React, { ComponentType, Fragment, useEffect, useState } from 'react';
|
||||
import styles from './children.styles';
|
||||
import { Icon, MainButton, SimpleChart } from '../../../components';
|
||||
import { useIntl } from 'react-intl';
|
||||
import EStyleSheet from 'react-native-extended-stylesheet';
|
||||
|
||||
export interface CoinCardProps {
|
||||
id: string;
|
||||
chartData: number[];
|
||||
name: string;
|
||||
notCrypto: boolean;
|
||||
symbol: string;
|
||||
currencySymbol: string;
|
||||
changePercent: number;
|
||||
currentValue: number;
|
||||
ownedTokens: number;
|
||||
unclaimedRewards: string;
|
||||
enableBuy?: boolean;
|
||||
isClaiming?: boolean;
|
||||
footerComponent: ComponentType<any>
|
||||
onCardPress: () => void;
|
||||
onClaimPress: () => void;
|
||||
}
|
||||
|
||||
export const CoinCard = ({
|
||||
id,
|
||||
notCrypto,
|
||||
chartData,
|
||||
name,
|
||||
currencySymbol,
|
||||
symbol,
|
||||
changePercent,
|
||||
currentValue,
|
||||
ownedTokens,
|
||||
footerComponent,
|
||||
unclaimedRewards,
|
||||
enableBuy,
|
||||
isClaiming,
|
||||
onCardPress,
|
||||
onClaimPress,
|
||||
}: CoinCardProps) => {
|
||||
|
||||
const intl = useIntl();
|
||||
|
||||
const [claimExpected, setClaimExpected] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isClaiming && claimExpected) {
|
||||
setClaimExpected(false);
|
||||
}
|
||||
}, [isClaiming])
|
||||
|
||||
|
||||
const _onClaimPress = () => {
|
||||
setClaimExpected(unclaimedRewards ? true : false)
|
||||
onClaimPress();
|
||||
}
|
||||
|
||||
const _renderHeader = (
|
||||
<View style={styles.cardHeader}>
|
||||
{/* <View style={styles.logo} /> */}
|
||||
<View style={styles.cardTitleContainer}>
|
||||
<Text style={styles.textTitle} >{symbol}</Text>
|
||||
<Text style={styles.textSubtitle}>{intl.formatMessage({ id: `wallet.${id}.name` })}</Text>
|
||||
</View>
|
||||
<View style={styles.cardValuesContainer}>
|
||||
<Text
|
||||
style={styles.textTitle}>
|
||||
{`${ownedTokens.toFixed(3)} ${symbol}`}
|
||||
</Text>
|
||||
<Text style={styles.textSubtitleRight}>
|
||||
{`${(ownedTokens * currentValue).toFixed(2)}${currencySymbol}`}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
</View>
|
||||
);
|
||||
|
||||
|
||||
const _renderClaimSection = () => {
|
||||
if (unclaimedRewards || enableBuy) {
|
||||
const btnTitle = unclaimedRewards
|
||||
? unclaimedRewards
|
||||
: intl.formatMessage({ id: `wallet.${id}.buy` });
|
||||
return (
|
||||
<View style={styles.claimContainer}>
|
||||
<MainButton
|
||||
isLoading={isClaiming && claimExpected}
|
||||
isDisable={isClaiming && claimExpected}
|
||||
style={styles.claimBtn}
|
||||
height={50}
|
||||
onPress={_onClaimPress}
|
||||
>
|
||||
<Fragment>
|
||||
<Text style={styles.claimBtnTitle}>
|
||||
{btnTitle}
|
||||
</Text>
|
||||
<View style={styles.claimIconWrapper}>
|
||||
<Icon name="add" iconType="MaterialIcons" color={EStyleSheet.value('$primaryBlue')} size={23} />
|
||||
</View>
|
||||
</Fragment>
|
||||
</MainButton>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const _renderGraph = () => {
|
||||
const _baseWidth = Dimensions.get("window").width - 32;
|
||||
return (
|
||||
<View style={styles.chartContainer}>
|
||||
<SimpleChart
|
||||
data={chartData}
|
||||
baseWidth={_baseWidth}
|
||||
showLine={false}
|
||||
chartHeight={130}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const _renderFooter = (
|
||||
<View style={styles.cardFooter}>
|
||||
<Text style={styles.textCurValue}>{`${currencySymbol} ${currentValue.toFixed(2)}`}</Text>
|
||||
<Text style={changePercent > 0 ? styles.textDiffPositive : styles.textDiffNegative}>{`${changePercent >= 0 ? '+' : ''}${changePercent.toFixed(1)}%`}</Text>
|
||||
</View>
|
||||
)
|
||||
|
||||
return (
|
||||
<TouchableOpacity onPress={onCardPress} >
|
||||
<View style={styles.cardContainer}>
|
||||
{_renderHeader}
|
||||
{_renderClaimSection()}
|
||||
{!notCrypto && _renderGraph()}
|
||||
{!notCrypto ? _renderFooter : <View style={{ height: 12 }} />}
|
||||
{footerComponent && footerComponent}
|
||||
</View>
|
||||
|
||||
</TouchableOpacity>
|
||||
|
||||
);
|
||||
};
|
||||
|
1
src/screens/wallet/children/index.ts
Normal file
1
src/screens/wallet/children/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './coinCard';
|
@ -60,7 +60,7 @@ const HpView = ({ handleOnSelected, index, currentIndex, refreshing: reload }) =
|
||||
value: <FormattedCurrency isApproximate isToken value={estimatedHpValue} />,
|
||||
},
|
||||
{
|
||||
textKey: 'estimated_amount',
|
||||
textKey: 'vote_value',
|
||||
value: <FormattedCurrency isApproximate value={estimatedAmount} />,
|
||||
},
|
||||
]}
|
||||
|
@ -1,192 +0,0 @@
|
||||
/* eslint-disable react/jsx-wrap-multilines */
|
||||
import React, { Fragment, useState, useEffect } from 'react';
|
||||
import Swiper from 'react-native-swiper';
|
||||
import { SafeAreaView, View, RefreshControl, Text } from 'react-native';
|
||||
|
||||
// Containers
|
||||
import { FlatList } from 'react-native-gesture-handler';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { LoggedInContainer } from '../../../containers';
|
||||
|
||||
// Components
|
||||
import {
|
||||
Header,
|
||||
Transaction,
|
||||
HorizontalIconList,
|
||||
ListPlaceHolder,
|
||||
CollapsibleCard,
|
||||
} from '../../../components';
|
||||
import EstmView from './estmView';
|
||||
import HiveView from './hiveView';
|
||||
import HpView from './hpView';
|
||||
import HbdView from './hbdView';
|
||||
|
||||
// Styles
|
||||
import globalStyles from '../../../globalStyles';
|
||||
import styles from './walletScreenStyles';
|
||||
|
||||
import POINTS, { POINTS_KEYS } from '../../../constants/options/points';
|
||||
import { useAppSelector } from '../../../hooks';
|
||||
|
||||
const HEADER_EXPANDED_HEIGHT = 312;
|
||||
|
||||
const WalletScreen = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
const isDarkTheme = useAppSelector((state) => state.application.isDarkTheme);
|
||||
|
||||
const [selectedUserActivities, setSelectedUserActivities] = useState(null);
|
||||
const [filteredActivites, setFilteredActivites] = useState([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [currentIndex, setCurrentIndex] = useState(0);
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
const [isExpanded, setIsExpanded] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedUserActivities) {
|
||||
const activities = selectedUserActivities
|
||||
? selectedUserActivities.filter((item) => {
|
||||
return (
|
||||
item &&
|
||||
item.value &&
|
||||
item.value.indexOf(['Points', 'HIVE', 'HBD', 'HP'][currentIndex]) > -1
|
||||
);
|
||||
})
|
||||
: [];
|
||||
setFilteredActivites(activities);
|
||||
} else {
|
||||
setFilteredActivites([]);
|
||||
}
|
||||
}, [selectedUserActivities]);
|
||||
|
||||
const _handleSwipeItemChange = (userActivities, _isLoading) => {
|
||||
setSelectedUserActivities(userActivities);
|
||||
setIsLoading(_isLoading);
|
||||
setRefreshing(false);
|
||||
};
|
||||
|
||||
const _renderHeaderComponent = () => {
|
||||
return (
|
||||
<>
|
||||
<CollapsibleCard
|
||||
expanded={true}
|
||||
isExpanded={isExpanded}
|
||||
noContainer
|
||||
noBorder
|
||||
locked
|
||||
titleComponent={<View />}
|
||||
handleOnExpanded={() => {
|
||||
setIsExpanded(true);
|
||||
}}
|
||||
>
|
||||
<View style={[styles.header, { height: HEADER_EXPANDED_HEIGHT }]}>
|
||||
<Swiper
|
||||
loop={false}
|
||||
showsPagination={true}
|
||||
index={0}
|
||||
dotStyle={styles.dotStyle}
|
||||
onIndexChanged={(index) => setCurrentIndex(index)}
|
||||
>
|
||||
<EstmView
|
||||
index={0}
|
||||
handleOnSelected={_handleSwipeItemChange}
|
||||
refreshing={refreshing}
|
||||
currentIndex={currentIndex}
|
||||
/>
|
||||
<HiveView
|
||||
index={1}
|
||||
handleOnSelected={_handleSwipeItemChange}
|
||||
refreshing={refreshing}
|
||||
currentIndex={currentIndex}
|
||||
/>
|
||||
<HbdView
|
||||
index={2}
|
||||
handleOnSelected={_handleSwipeItemChange}
|
||||
refreshing={refreshing}
|
||||
currentIndex={currentIndex}
|
||||
/>
|
||||
<HpView
|
||||
index={3}
|
||||
refreshing={refreshing}
|
||||
handleOnSelected={_handleSwipeItemChange}
|
||||
currentIndex={currentIndex}
|
||||
/>
|
||||
</Swiper>
|
||||
</View>
|
||||
</CollapsibleCard>
|
||||
|
||||
<SafeAreaView style={styles.header}>
|
||||
{currentIndex === 0 && <HorizontalIconList options={POINTS} optionsKeys={POINTS_KEYS} />}
|
||||
</SafeAreaView>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const _renderItem = ({ item, index }) => {
|
||||
return <Transaction type={currentIndex} item={item} index={index} />;
|
||||
};
|
||||
|
||||
const _renderLoading = () => {
|
||||
if (isLoading) {
|
||||
return <ListPlaceHolder />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Text style={globalStyles.hintText}>{intl.formatMessage({ id: 'wallet.no_activity' })}</Text>
|
||||
);
|
||||
};
|
||||
|
||||
const _refreshControl = (
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={() => setRefreshing(true)}
|
||||
progressBackgroundColor="#357CE6"
|
||||
tintColor={!isDarkTheme ? '#357ce6' : '#96c0ff'}
|
||||
titleColor="#fff"
|
||||
colors={['#fff']}
|
||||
/>
|
||||
);
|
||||
|
||||
const _onScroll = (evt) => {
|
||||
console.log(evt);
|
||||
const expanded = evt.nativeEvent.contentOffset.y < 10;
|
||||
if (isExpanded !== expanded) {
|
||||
setIsExpanded(expanded);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Header />
|
||||
<SafeAreaView style={globalStyles.defaultContainer}>
|
||||
<LoggedInContainer>
|
||||
{() => (
|
||||
<>
|
||||
{_renderHeaderComponent()}
|
||||
<View style={globalStyles.listWrapper}>
|
||||
<FlatList
|
||||
data={filteredActivites}
|
||||
style={globalStyles.tabBarBottom}
|
||||
ListEmptyComponent={_renderLoading}
|
||||
renderItem={_renderItem}
|
||||
keyExtractor={(item, index) => index.toString()}
|
||||
maxToRenderPerBatch={5}
|
||||
initialNumToRender={5}
|
||||
refreshControl={_refreshControl}
|
||||
windowSize={5}
|
||||
onScroll={_onScroll}
|
||||
onScrollToTop={() => {
|
||||
setIsExpanded(true);
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
</LoggedInContainer>
|
||||
</SafeAreaView>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default WalletScreen;
|
||||
/* eslint-enable */
|
290
src/screens/wallet/screen/walletScreen.tsx
Normal file
290
src/screens/wallet/screen/walletScreen.tsx
Normal file
@ -0,0 +1,290 @@
|
||||
/* eslint-disable react/jsx-wrap-multilines */
|
||||
import React, { Fragment, useState, useEffect, useRef } from 'react';
|
||||
import { SafeAreaView, View, RefreshControl, Text, Alert, AppState, AppStateStatus } from 'react-native';
|
||||
|
||||
// Containers
|
||||
import { FlatList } from 'react-native-gesture-handler';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { LoggedInContainer } from '../../../containers';
|
||||
|
||||
// Components
|
||||
import {
|
||||
Header,
|
||||
HorizontalIconList,
|
||||
PostCardPlaceHolder,
|
||||
} from '../../../components';
|
||||
|
||||
|
||||
// Styles
|
||||
import globalStyles from '../../../globalStyles';
|
||||
import styles from './walletScreenStyles';
|
||||
|
||||
import { useAppDispatch, useAppSelector } from '../../../hooks';
|
||||
import {CoinCard} from '../children';
|
||||
import { fetchMarketChart, INTERVAL_HOURLY } from '../../../providers/coingecko/coingecko';
|
||||
import { withNavigation } from 'react-navigation';
|
||||
import ROUTES from '../../../constants/routeNames';
|
||||
import { CoinDetailsScreenParams } from '../../coinDetails/screen/coinDetailsScreen';
|
||||
import POINTS, { POINTS_KEYS } from '../../../constants/options/points';
|
||||
import { CoinBase, CoinData } from '../../../redux/reducers/walletReducer';
|
||||
import { fetchAndSetCoinsData, fetchCoinQuotes, resetWalletData, setPriceHistory } from '../../../redux/actions/walletActions';
|
||||
import { COIN_IDS } from '../../../constants/defaultCoins';
|
||||
import { claimPoints } from '../../../providers/ecency/ePoint';
|
||||
import { claimRewardBalance, getAccount } from '../../../providers/hive/dhive';
|
||||
import { toastNotification } from '../../../redux/actions/uiAction';
|
||||
import moment from 'moment';
|
||||
|
||||
|
||||
const CHART_DAYS_RANGE = 1;
|
||||
|
||||
const WalletScreen = ({navigation}) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
//refs
|
||||
const appState = useRef(AppState.currentState);
|
||||
|
||||
//redux
|
||||
const isDarkTheme = useAppSelector((state) => state.application.isDarkTheme);
|
||||
const currency = useAppSelector((state)=>state.application.currency);
|
||||
|
||||
const {
|
||||
selectedCoins,
|
||||
priceHistories,
|
||||
coinsData,
|
||||
updateTimestamp,
|
||||
quotes,
|
||||
...wallet
|
||||
} = useAppSelector((state)=>state.wallet);
|
||||
|
||||
const currentAccount = useAppSelector((state)=>state.account.currentAccount);
|
||||
const pinHash = useAppSelector((state)=>state.application.pin);
|
||||
|
||||
//state
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
const [isClaiming, setIsClaiming] = useState(false);
|
||||
|
||||
|
||||
//side-effects
|
||||
useEffect(()=>{
|
||||
AppState.addEventListener('change', _handleAppStateChange);
|
||||
_fetchData();
|
||||
|
||||
return _cleanup;
|
||||
},[])
|
||||
|
||||
useEffect(()=>{
|
||||
if(currency.currency !== wallet.vsCurrency || currentAccount.username !== wallet.username ){
|
||||
dispatch(resetWalletData());
|
||||
_fetchData(true);
|
||||
}
|
||||
},[currency, currentAccount])
|
||||
|
||||
|
||||
const _cleanup = () => {
|
||||
AppState.removeEventListener('change', _handleAppStateChange);
|
||||
}
|
||||
|
||||
|
||||
//actions
|
||||
const _handleAppStateChange = (nextAppState:AppStateStatus) => {
|
||||
if (appState.current.match(/inactive|background/) && nextAppState === 'active') {
|
||||
console.log('updating selected coins data on app resume')
|
||||
_fetchCoinsData(true)
|
||||
}
|
||||
appState.current = nextAppState;
|
||||
};
|
||||
|
||||
|
||||
|
||||
const _fetchData = (refresh?:boolean) => {
|
||||
if(!isLoading){
|
||||
_fetchPriceHistory();
|
||||
_fetchCoinsData(refresh);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const _fetchPriceHistory = () => {
|
||||
selectedCoins.forEach(async (token:CoinBase)=>{
|
||||
|
||||
const expiresAt = priceHistories[token.id]?.expiresAt || 0;
|
||||
const curTime = new Date().getTime();
|
||||
|
||||
if(!token.notCrypto && curTime > expiresAt ){
|
||||
const marketChart = await fetchMarketChart(token.id, currency.currency, CHART_DAYS_RANGE, INTERVAL_HOURLY);
|
||||
const priceData = marketChart.prices.map(item=>item.yValue);
|
||||
dispatch(setPriceHistory(token.id, currency.currency, priceData));
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const _fetchCoinsData = async (refresh?:boolean) => {
|
||||
setIsLoading(true);
|
||||
if(refresh || !quotes){
|
||||
dispatch(fetchCoinQuotes());
|
||||
}
|
||||
await dispatch(fetchAndSetCoinsData(refresh));
|
||||
|
||||
setRefreshing(false);
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
|
||||
const _claimEcencyPoints = async () => {
|
||||
setIsClaiming(true);
|
||||
try{
|
||||
await claimPoints()
|
||||
await _fetchCoinsData(true);
|
||||
}catch(error){
|
||||
Alert.alert(`${error.message}\nTry again or write to support@ecency.com`);
|
||||
}
|
||||
setIsClaiming(false);
|
||||
};
|
||||
|
||||
|
||||
const _claimRewardBalance = async () => {
|
||||
setIsClaiming(true);
|
||||
try{
|
||||
const account = await getAccount(currentAccount.name);
|
||||
await claimRewardBalance(
|
||||
currentAccount,
|
||||
pinHash,
|
||||
account.reward_hive_balance,
|
||||
account.reward_hbd_balance,
|
||||
account.reward_vesting_balance,
|
||||
)
|
||||
await _fetchCoinsData(true);
|
||||
dispatch(
|
||||
toastNotification(
|
||||
intl.formatMessage({
|
||||
id: 'alert.claim_reward_balance_ok',
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
}catch(error){
|
||||
Alert.alert(intl.formatMessage(
|
||||
{id:'alert.claim_failed'},
|
||||
{message:error.message}
|
||||
));
|
||||
}
|
||||
setIsClaiming(false);
|
||||
}
|
||||
|
||||
|
||||
const _claimRewards = (coinId:string) => {
|
||||
if(isLoading){
|
||||
setRefreshing(true);
|
||||
Alert.alert(intl.formatMessage({id:'alert.wallet_updating'}) );
|
||||
return;
|
||||
}
|
||||
switch(coinId){
|
||||
case COIN_IDS.ECENCY:
|
||||
_claimEcencyPoints();
|
||||
break;
|
||||
|
||||
case COIN_IDS.HP:
|
||||
_claimRewardBalance()
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const _renderItem = ({ item, index }:{item:CoinBase, index:number}) => {
|
||||
const coinData:CoinData = coinsData[item.id] || {};
|
||||
|
||||
const _tokenMarketData:number[] = priceHistories[item.id] ? priceHistories[item.id].data : [];
|
||||
|
||||
const _balance = coinData.balance + (coinData.savings || 0);
|
||||
const quote = quotes ? quotes[item.id] : {};
|
||||
|
||||
const _onCardPress = () => {
|
||||
navigation.navigate(ROUTES.SCREENS.COIN_DETAILS, {
|
||||
coinId:item.id
|
||||
} as CoinDetailsScreenParams)
|
||||
}
|
||||
|
||||
const _onClaimPress = () => {
|
||||
if(coinData.unclaimedBalance){
|
||||
_claimRewards(item.id);
|
||||
} else if(item.id === COIN_IDS.ECENCY) {
|
||||
navigation.navigate(ROUTES.SCREENS.BOOST)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<CoinCard
|
||||
chartData={_tokenMarketData || []}
|
||||
currentValue={quote.price || 0}
|
||||
changePercent={quote.percentChange || 0}
|
||||
currencySymbol={currency.currencySymbol}
|
||||
ownedTokens={_balance}
|
||||
unclaimedRewards={coinData.unclaimedBalance}
|
||||
enableBuy={!coinData.unclaimedBalance && item.id === COIN_IDS.ECENCY}
|
||||
isClaiming={isClaiming}
|
||||
onCardPress={_onCardPress}
|
||||
onClaimPress={_onClaimPress}
|
||||
footerComponent={index === 0 && <HorizontalIconList options={POINTS} optionsKeys={POINTS_KEYS} />}
|
||||
{...item} />
|
||||
);
|
||||
};
|
||||
|
||||
const _renderHeader = () => {
|
||||
return (
|
||||
<View style={styles.header}>
|
||||
<Text style={styles.lastUpdateText}>
|
||||
{isLoading
|
||||
? intl.formatMessage({id:'wallet.updating'})
|
||||
:`${intl.formatMessage({id:'wallet.last_updated'})} ${moment(updateTimestamp).format('HH:mm:ss')}`}
|
||||
</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
const _refreshControl = (
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={() => {_fetchData(true); setRefreshing(true)}}
|
||||
progressBackgroundColor="#357CE6"
|
||||
tintColor={!isDarkTheme ? '#357ce6' : '#96c0ff'}
|
||||
titleColor="#fff"
|
||||
colors={['#fff']}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Header />
|
||||
<SafeAreaView style={globalStyles.defaultContainer}>
|
||||
<LoggedInContainer>
|
||||
{() => (
|
||||
<View style={styles.listWrapper}>
|
||||
<FlatList
|
||||
data={updateTimestamp ? selectedCoins : []}
|
||||
extraData={[coinsData, priceHistories]}
|
||||
style={globalStyles.tabBarBottom}
|
||||
ListEmptyComponent={<PostCardPlaceHolder />}
|
||||
ListHeaderComponent={_renderHeader}
|
||||
renderItem={_renderItem}
|
||||
keyExtractor={(item, index) => index.toString()}
|
||||
refreshControl={_refreshControl}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
</LoggedInContainer>
|
||||
</SafeAreaView>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default withNavigation(WalletScreen);
|
||||
/* eslint-enable */
|
@ -5,9 +5,18 @@ export default EStyleSheet.create({
|
||||
padding: 0,
|
||||
},
|
||||
header: {
|
||||
backgroundColor: '$primaryBackgroundColor',
|
||||
alignItems: 'flex-end',
|
||||
paddingHorizontal: 16,
|
||||
},
|
||||
lastUpdateText: {
|
||||
color: '$iconColor',
|
||||
fontSize: 10,
|
||||
},
|
||||
dotStyle: {
|
||||
backgroundColor: '$primaryDarkText',
|
||||
},
|
||||
listWrapper: {
|
||||
backgroundColor: '$primaryBackgroundColor',
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
|
@ -17,6 +17,7 @@ export default {
|
||||
$primaryDarkGray: '#c1c5c7',
|
||||
$primaryLightGray: '#f6f6f6',
|
||||
$primaryRed: '#e63535',
|
||||
$primaryGreen: '#4FD688',
|
||||
$companyRed: '#c10000',
|
||||
$primaryBlack: '#c1c5c7',
|
||||
$primaryDarkText: '#526d91',
|
||||
@ -42,6 +43,9 @@ export default {
|
||||
$noConnectionColor: '#788187',
|
||||
$borderedButtonBlue: '#5CCDFF',
|
||||
|
||||
$chartBlue: '#357CE6',
|
||||
$chartText: '#f5f5f5',
|
||||
|
||||
// Devices Sizes
|
||||
$deviceHeight:
|
||||
Platform.OS === 'ios'
|
||||
|
@ -17,6 +17,7 @@ export default {
|
||||
$primaryDarkGray: '#788187',
|
||||
$primaryLightGray: '#f6f6f6',
|
||||
$primaryRed: '#e63535',
|
||||
$primaryGreen: '#4FD688',
|
||||
$companyRed: '#c10000',
|
||||
$primaryBlack: '#3c4449',
|
||||
$primaryDarkText: '#788187',
|
||||
@ -42,6 +43,9 @@ export default {
|
||||
$noConnectionColor: '#788187',
|
||||
$borderedButtonBlue: '#357ce6',
|
||||
|
||||
$chartBlue: '#357CE6',
|
||||
$chartText: '#357ce6',
|
||||
|
||||
// Devices Sizes
|
||||
$deviceHeight:
|
||||
Platform.OS === 'ios'
|
||||
|
33
src/utils/getCurrentHpApr.ts
Normal file
33
src/utils/getCurrentHpApr.ts
Normal file
@ -0,0 +1,33 @@
|
||||
|
||||
export const getCurrentHpApr = (gprops) => {
|
||||
// The inflation was set to 9.5% at block 7m
|
||||
const initialInflationRate = 9.5;
|
||||
const initialBlock = 7000000;
|
||||
|
||||
// It decreases by 0.01% every 250k blocks
|
||||
const decreaseRate = 250000;
|
||||
const decreasePercentPerIncrement = 0.01;
|
||||
|
||||
// How many increments have happened since block 7m?
|
||||
const headBlock = gprops.headBlock;
|
||||
const deltaBlocks = headBlock - initialBlock;
|
||||
const decreaseIncrements = deltaBlocks / decreaseRate;
|
||||
|
||||
// Current inflation rate
|
||||
let currentInflationRate =
|
||||
initialInflationRate -
|
||||
decreaseIncrements * decreasePercentPerIncrement;
|
||||
|
||||
// Cannot go lower than 0.95%
|
||||
if (currentInflationRate < 0.95) {
|
||||
currentInflationRate = 0.95;
|
||||
}
|
||||
|
||||
// Now lets calculate the "APR"
|
||||
const vestingRewardPercent = gprops.vestingRewardPercent / 10000;
|
||||
const virtualSupply = gprops.virtualSupply;
|
||||
const totalVestingFunds = gprops.totalVestingFund;
|
||||
return (
|
||||
(virtualSupply * currentInflationRate * vestingRewardPercent) / totalVestingFunds
|
||||
);
|
||||
};
|
@ -1,288 +0,0 @@
|
||||
import get from 'lodash/get';
|
||||
import parseDate from './parseDate';
|
||||
import parseToken from './parseToken';
|
||||
import { vestsToHp } from './conversions';
|
||||
import { getFeedHistory, getAccount, getAccountHistory } from '../providers/hive/dhive';
|
||||
import { getCurrencyTokenRate } from '../providers/ecency/ecency';
|
||||
|
||||
export const transferTypes = [
|
||||
'curation_reward',
|
||||
'author_reward',
|
||||
'comment_benefactor_reward',
|
||||
'claim_reward_balance',
|
||||
'transfer',
|
||||
'transfer_to_savings',
|
||||
'transfer_from_savings',
|
||||
'transfer_to_vesting',
|
||||
'withdraw_vesting',
|
||||
'fill_order',
|
||||
'escrow_transfer',
|
||||
'escrow_dispute',
|
||||
'escrow_release',
|
||||
'escrow_approve',
|
||||
'delegate_vesting_shares',
|
||||
'cancel_transfer_from_savings',
|
||||
'fill_convert_request',
|
||||
'fill_transfer_from_savings',
|
||||
'fill_vesting_withdraw',
|
||||
];
|
||||
|
||||
export const groomingTransactionData = (transaction, hivePerMVests) => {
|
||||
if (!transaction || !hivePerMVests) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const result = {
|
||||
iconType: 'MaterialIcons',
|
||||
};
|
||||
|
||||
[result.textKey] = transaction[1].op;
|
||||
const opData = transaction[1].op[1];
|
||||
const { timestamp } = transaction[1];
|
||||
|
||||
result.created = timestamp;
|
||||
result.icon = 'local-activity';
|
||||
|
||||
//TODO: Format other wallet related operations
|
||||
|
||||
switch (result.textKey) {
|
||||
case 'curation_reward':
|
||||
const { reward } = opData;
|
||||
const { comment_author: commentAuthor, comment_permlink: commentPermlink } = opData;
|
||||
|
||||
result.value = `${vestsToHp(parseToken(reward), hivePerMVests)
|
||||
.toFixed(3)
|
||||
.replace(',', '.')} HP`;
|
||||
result.details = commentAuthor ? `@${commentAuthor}/${commentPermlink}` : null;
|
||||
break;
|
||||
case 'author_reward':
|
||||
case 'comment_benefactor_reward':
|
||||
let {
|
||||
hbd_payout: hbdPayout,
|
||||
hive_payout: hivePayout,
|
||||
vesting_payout: vestingPayout,
|
||||
} = opData;
|
||||
|
||||
const { author, permlink } = opData;
|
||||
|
||||
hbdPayout = parseToken(hbdPayout).toFixed(3).replace(',', '.');
|
||||
hivePayout = parseToken(hivePayout).toFixed(3).replace(',', '.');
|
||||
vestingPayout = vestsToHp(parseToken(vestingPayout), hivePerMVests)
|
||||
.toFixed(3)
|
||||
.replace(',', '.');
|
||||
|
||||
result.value = `${hbdPayout > 0 ? `${hbdPayout} HBD` : ''} ${
|
||||
hivePayout > 0 ? `${hivePayout} HIVE` : ''
|
||||
} ${vestingPayout > 0 ? `${vestingPayout} HP` : ''}`;
|
||||
|
||||
result.details = author && permlink ? `@${author}/${permlink}` : null;
|
||||
if (result.textKey === 'comment_benefactor_reward') {
|
||||
result.icon = 'comment';
|
||||
}
|
||||
break;
|
||||
case 'claim_reward_balance':
|
||||
let { reward_hbd: rewardHdb, reward_hive: rewardHive, reward_vests: rewardVests } = opData;
|
||||
|
||||
rewardHdb = parseToken(rewardHdb).toFixed(3).replace(',', '.');
|
||||
rewardHive = parseToken(rewardHive).toFixed(3).replace(',', '.');
|
||||
rewardVests = vestsToHp(parseToken(rewardVests), hivePerMVests).toFixed(3).replace(',', '.');
|
||||
|
||||
result.value = `${rewardHdb > 0 ? `${rewardHdb} HBD` : ''} ${
|
||||
rewardHive > 0 ? `${rewardHive} HIVE` : ''
|
||||
} ${rewardVests > 0 ? `${rewardVests} HP` : ''}`;
|
||||
break;
|
||||
case 'transfer':
|
||||
case 'transfer_to_savings':
|
||||
case 'transfer_from_savings':
|
||||
case 'transfer_to_vesting':
|
||||
const { amount, memo, from, to } = opData;
|
||||
|
||||
result.value = `${amount}`;
|
||||
result.icon = 'compare-arrows';
|
||||
result.details = from && to ? `@${from} to @${to}` : null;
|
||||
result.memo = memo || null;
|
||||
break;
|
||||
case 'withdraw_vesting':
|
||||
const { acc } = opData;
|
||||
let { vesting_shares: opVestingShares } = opData;
|
||||
|
||||
opVestingShares = parseToken(opVestingShares);
|
||||
result.value = `${vestsToHp(opVestingShares, hivePerMVests).toFixed(3).replace(',', '.')} HP`;
|
||||
result.icon = 'attach-money';
|
||||
result.details = acc ? `@${acc}` : null;
|
||||
break;
|
||||
case 'fill_order':
|
||||
const { current_pays: currentPays, open_pays: openPays } = opData;
|
||||
|
||||
result.value = `${currentPays} = ${openPays}`;
|
||||
result.icon = 'reorder';
|
||||
break;
|
||||
case 'escrow_transfer':
|
||||
case 'escrow_dispute':
|
||||
case 'escrow_release':
|
||||
case 'escrow_approve':
|
||||
const { agent, escrow_id } = opData;
|
||||
let { from: frome } = opData;
|
||||
let { to: toe } = opData;
|
||||
|
||||
result.value = `${escrow_id}`;
|
||||
result.icon = 'wb-iridescent';
|
||||
result.details = frome && toe ? `@${frome} to @${toe}` : null;
|
||||
result.memo = agent || null;
|
||||
break;
|
||||
case 'delegate_vesting_shares':
|
||||
const { delegator, delegatee, vesting_shares } = opData;
|
||||
|
||||
result.value = `${vesting_shares}`;
|
||||
result.icon = 'change-history';
|
||||
result.details = delegatee && delegator ? `@${delegator} to @${delegatee}` : null;
|
||||
break;
|
||||
case 'cancel_transfer_from_savings':
|
||||
let { from: from_who, request_id: requestId } = opData;
|
||||
|
||||
result.value = `${0}`;
|
||||
result.icon = 'cancel';
|
||||
result.details = from_who ? `from @${from_who}, id: ${requestId}` : null;
|
||||
break;
|
||||
case 'fill_convert_request':
|
||||
let { owner: who, requestid: requestedId, amount_out: amount_out } = opData;
|
||||
|
||||
result.value = `${amount_out}`;
|
||||
result.icon = 'hourglass-full';
|
||||
result.details = who ? `@${who}, id: ${requestedId}` : null;
|
||||
break;
|
||||
case 'fill_transfer_from_savings':
|
||||
let { from: fillwho, to: fillto, amount: fillamount, request_id: fillrequestId } = opData;
|
||||
|
||||
result.value = `${fillamount}`;
|
||||
result.icon = 'hourglass-full';
|
||||
result.details = fillwho ? `@${fillwho} to @${fillto}, id: ${fillrequestId}` : null;
|
||||
break;
|
||||
case 'fill_vesting_withdraw':
|
||||
let { from_account: pd_who, to_account: pd_to, deposited: deposited } = opData;
|
||||
|
||||
result.value = `${deposited}`;
|
||||
result.icon = 'hourglass-full';
|
||||
result.details = pd_who ? `@${pd_who} to ${pd_to}` : null;
|
||||
break;
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export const groomingWalletData = async (user, globalProps, userCurrency) => {
|
||||
const walletData = {};
|
||||
|
||||
if (!user) {
|
||||
return walletData;
|
||||
}
|
||||
const state = await getAccount(get(user, 'name'));
|
||||
|
||||
//const { accounts } = state;
|
||||
//if (!accounts) {
|
||||
// return walletData;
|
||||
//}
|
||||
const [userdata] = state;
|
||||
|
||||
// TODO: move them to utils these so big for a lifecycle function
|
||||
walletData.rewardHiveBalance = parseToken(userdata.reward_hive_balance);
|
||||
walletData.rewardHbdBalance = parseToken(userdata.reward_hbd_balance);
|
||||
walletData.rewardVestingHive = parseToken(userdata.reward_vesting_hive);
|
||||
walletData.hasUnclaimedRewards =
|
||||
walletData.rewardHiveBalance > 0 ||
|
||||
walletData.rewardHbdBalance > 0 ||
|
||||
walletData.rewardVestingHive > 0;
|
||||
walletData.balance = parseToken(userdata.balance);
|
||||
walletData.vestingShares = parseToken(userdata.vesting_shares);
|
||||
walletData.vestingSharesDelegated = parseToken(userdata.delegated_vesting_shares);
|
||||
walletData.vestingSharesReceived = parseToken(userdata.received_vesting_shares);
|
||||
walletData.vestingSharesTotal =
|
||||
walletData.vestingShares - walletData.vestingSharesDelegated + walletData.vestingSharesReceived;
|
||||
walletData.hbdBalance = parseToken(userdata.hbd_balance);
|
||||
walletData.savingBalance = parseToken(userdata.savings_balance);
|
||||
walletData.savingBalanceHbd = parseToken(userdata.savings_hbd_balance);
|
||||
|
||||
const feedHistory = await getFeedHistory();
|
||||
const base = parseToken(feedHistory.current_median_history.base);
|
||||
const quote = parseToken(feedHistory.current_median_history.quote);
|
||||
|
||||
walletData.hivePerMVests = globalProps.hivePerMVests;
|
||||
|
||||
const pricePerHive = base / quote;
|
||||
|
||||
const totalHive =
|
||||
vestsToHp(walletData.vestingShares, walletData.hivePerMVests) +
|
||||
walletData.balance +
|
||||
walletData.savingBalance;
|
||||
|
||||
const totalHbd = walletData.hbdBalance + walletData.savingBalanceHbd;
|
||||
|
||||
walletData.estimatedValue = totalHive * pricePerHive + totalHbd;
|
||||
|
||||
const ppHbd = await getCurrencyTokenRate(userCurrency, 'hbd');
|
||||
const ppHive = await getCurrencyTokenRate(userCurrency, 'hive');
|
||||
|
||||
walletData.estimatedHiveValue = (walletData.balance + walletData.savingBalance) * ppHive;
|
||||
walletData.estimatedHbdValue = totalHbd * ppHbd;
|
||||
walletData.estimatedHpValue =
|
||||
vestsToHp(walletData.vestingShares, walletData.hivePerMVests) * ppHive;
|
||||
|
||||
walletData.showPowerDown = userdata.next_vesting_withdrawal !== '1969-12-31T23:59:59';
|
||||
const timeDiff = Math.abs(parseDate(userdata.next_vesting_withdrawal) - new Date());
|
||||
walletData.nextVestingWithdrawal = Math.round(timeDiff / (1000 * 3600));
|
||||
|
||||
const history = await getAccountHistory(get(user, 'name'));
|
||||
|
||||
const transfers = history.filter((tx) => transferTypes.includes(get(tx[1], 'op[0]', false)));
|
||||
|
||||
transfers.sort(compare);
|
||||
|
||||
walletData.transactions = transfers;
|
||||
return walletData;
|
||||
};
|
||||
|
||||
function compare(a, b) {
|
||||
if (a[1].block < b[1].block) {
|
||||
return 1;
|
||||
}
|
||||
if (a[1].block > b[1].block) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export const groomingPointsTransactionData = (transaction) => {
|
||||
if (!transaction) {
|
||||
return null;
|
||||
}
|
||||
const result = {
|
||||
...transaction,
|
||||
};
|
||||
|
||||
result.details = get(transaction, 'sender')
|
||||
? `from @${get(transaction, 'sender')}`
|
||||
: get(transaction, 'receiver') && `to @${get(transaction, 'receiver')}`;
|
||||
|
||||
result.value = `${get(transaction, 'amount')} Points`;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const getPointsEstimate = async (amount, userCurrency) => {
|
||||
if (!amount) {
|
||||
return 0;
|
||||
}
|
||||
const ppEstm = await getCurrencyTokenRate(userCurrency, 'estm');
|
||||
|
||||
return ppEstm * amount;
|
||||
};
|
||||
|
||||
export const getBtcEstimate = async (amount, userCurrency) => {
|
||||
if (!amount) {
|
||||
return 0;
|
||||
}
|
||||
const ppBtc = await getCurrencyTokenRate(userCurrency, 'btc');
|
||||
|
||||
return ppBtc * amount;
|
||||
};
|
738
src/utils/wallet.ts
Normal file
738
src/utils/wallet.ts
Normal file
@ -0,0 +1,738 @@
|
||||
import get from 'lodash/get';
|
||||
import parseDate from './parseDate';
|
||||
import parseToken from './parseToken';
|
||||
import { vestsToHp } from './conversions';
|
||||
import { getAccount, getAccountHistory, getConversionRequests, getFeedHistory, getOpenOrders, getSavingsWithdrawFrom } from '../providers/hive/dhive';
|
||||
import { getCurrencyTokenRate, getLatestQuotes } from '../providers/ecency/ecency';
|
||||
import { CoinActivitiesCollection, CoinActivity, CoinBase, CoinData, DataPair, QuoteItem } from '../redux/reducers/walletReducer';
|
||||
import { GlobalProps } from '../redux/reducers/accountReducer';
|
||||
import { getEstimatedAmount } from './vote';
|
||||
import { getUser as getEcencyUser, getUserPoints } from '../providers/ecency/ePoint';
|
||||
// Constant
|
||||
import POINTS from '../constants/options/points';
|
||||
import { COIN_IDS } from '../constants/defaultCoins';
|
||||
import { operationOrders } from '@hiveio/dhive/lib/utils';
|
||||
import { ConversionRequest, OpenOrderItem, OrdersData, SavingsWithdrawRequest } from '../providers/hive/hive.types';
|
||||
import parseAsset from './parseAsset';
|
||||
import { utils } from '@hiveio/dhive';
|
||||
|
||||
|
||||
export const transferTypes = [
|
||||
'curation_reward',
|
||||
'author_reward',
|
||||
'comment_benefactor_reward',
|
||||
'claim_reward_balance',
|
||||
'transfer',
|
||||
'transfer_to_savings',
|
||||
'transfer_from_savings',
|
||||
'transfer_to_vesting',
|
||||
'withdraw_vesting',
|
||||
'fill_order',
|
||||
'escrow_transfer',
|
||||
'escrow_dispute',
|
||||
'escrow_release',
|
||||
'escrow_approve',
|
||||
'delegate_vesting_shares',
|
||||
'cancel_transfer_from_savings',
|
||||
'fill_convert_request',
|
||||
'fill_transfer_from_savings',
|
||||
'fill_vesting_withdraw',
|
||||
];
|
||||
|
||||
const ECENCY_ACTIONS = [
|
||||
'dropdown_transfer', 'dropdown_promote', 'dropdown_boost'
|
||||
];
|
||||
const HIVE_ACTIONS = [
|
||||
'purchase_estm',
|
||||
'transfer_token',
|
||||
'transfer_to_savings',
|
||||
'transfer_to_vesting',
|
||||
'withdraw_hive'
|
||||
];
|
||||
const HBD_ACTIONS = [
|
||||
'purchase_estm',
|
||||
'transfer_token',
|
||||
'transfer_to_savings',
|
||||
'convert',
|
||||
'withdraw_hbd'
|
||||
];
|
||||
const HIVE_POWER_ACTIONS = ['delegate', 'power_down'];
|
||||
|
||||
|
||||
export const groomingTransactionData = (transaction, hivePerMVests) => {
|
||||
if (!transaction || !hivePerMVests) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const result = {
|
||||
iconType: 'MaterialIcons',
|
||||
};
|
||||
|
||||
[result.textKey] = transaction[1].op;
|
||||
const opData = transaction[1].op[1];
|
||||
const { timestamp } = transaction[1];
|
||||
|
||||
result.created = timestamp;
|
||||
result.icon = 'local-activity';
|
||||
|
||||
//TODO: Format other wallet related operations
|
||||
|
||||
switch (result.textKey) {
|
||||
case 'curation_reward':
|
||||
const { reward } = opData;
|
||||
const { comment_author: commentAuthor, comment_permlink: commentPermlink } = opData;
|
||||
|
||||
result.value = `${vestsToHp(parseToken(reward), hivePerMVests)
|
||||
.toFixed(3)
|
||||
.replace(',', '.')} HP`;
|
||||
result.details = commentAuthor ? `@${commentAuthor}/${commentPermlink}` : null;
|
||||
break;
|
||||
case 'author_reward':
|
||||
case 'comment_benefactor_reward':
|
||||
let {
|
||||
hbd_payout: hbdPayout,
|
||||
hive_payout: hivePayout,
|
||||
vesting_payout: vestingPayout,
|
||||
} = opData;
|
||||
|
||||
const { author, permlink } = opData;
|
||||
|
||||
hbdPayout = parseToken(hbdPayout).toFixed(3).replace(',', '.');
|
||||
hivePayout = parseToken(hivePayout).toFixed(3).replace(',', '.');
|
||||
vestingPayout = vestsToHp(parseToken(vestingPayout), hivePerMVests)
|
||||
.toFixed(3)
|
||||
.replace(',', '.');
|
||||
|
||||
result.value = `${hbdPayout > 0 ? `${hbdPayout} HBD` : ''} ${hivePayout > 0 ? `${hivePayout} HIVE` : ''
|
||||
} ${vestingPayout > 0 ? `${vestingPayout} HP` : ''}`;
|
||||
|
||||
result.details = author && permlink ? `@${author}/${permlink}` : null;
|
||||
if (result.textKey === 'comment_benefactor_reward') {
|
||||
result.icon = 'comment';
|
||||
}
|
||||
break;
|
||||
case 'claim_reward_balance':
|
||||
let { reward_hbd: rewardHdb, reward_hive: rewardHive, reward_vests: rewardVests } = opData;
|
||||
|
||||
rewardHdb = parseToken(rewardHdb).toFixed(3).replace(',', '.');
|
||||
rewardHive = parseToken(rewardHive).toFixed(3).replace(',', '.');
|
||||
rewardVests = vestsToHp(parseToken(rewardVests), hivePerMVests).toFixed(3).replace(',', '.');
|
||||
|
||||
result.value = `${rewardHdb > 0 ? `${rewardHdb} HBD` : ''} ${rewardHive > 0 ? `${rewardHive} HIVE` : ''
|
||||
} ${rewardVests > 0 ? `${rewardVests} HP` : ''}`;
|
||||
break;
|
||||
case 'transfer':
|
||||
case 'transfer_to_savings':
|
||||
case 'transfer_from_savings':
|
||||
case 'transfer_to_vesting':
|
||||
const { amount, memo, from, to } = opData;
|
||||
|
||||
result.value = `${amount}`;
|
||||
result.icon = 'compare-arrows';
|
||||
result.details = from && to ? `@${from} to @${to}` : null;
|
||||
result.memo = memo || null;
|
||||
break;
|
||||
case 'withdraw_vesting':
|
||||
const { acc } = opData;
|
||||
let { vesting_shares: opVestingShares } = opData;
|
||||
|
||||
opVestingShares = parseToken(opVestingShares);
|
||||
result.value = `${vestsToHp(opVestingShares, hivePerMVests).toFixed(3).replace(',', '.')} HP`;
|
||||
result.icon = 'attach-money';
|
||||
result.details = acc ? `@${acc}` : null;
|
||||
break;
|
||||
case 'fill_order':
|
||||
const { current_pays: currentPays, open_pays: openPays } = opData;
|
||||
|
||||
result.value = `${currentPays} = ${openPays}`;
|
||||
result.icon = 'reorder';
|
||||
break;
|
||||
case 'escrow_transfer':
|
||||
case 'escrow_dispute':
|
||||
case 'escrow_release':
|
||||
case 'escrow_approve':
|
||||
const { agent, escrow_id } = opData;
|
||||
let { from: frome } = opData;
|
||||
let { to: toe } = opData;
|
||||
|
||||
result.value = `${escrow_id}`;
|
||||
result.icon = 'wb-iridescent';
|
||||
result.details = frome && toe ? `@${frome} to @${toe}` : null;
|
||||
result.memo = agent || null;
|
||||
break;
|
||||
case 'delegate_vesting_shares':
|
||||
const { delegator, delegatee, vesting_shares } = opData;
|
||||
|
||||
result.value = `${vesting_shares}`;
|
||||
result.icon = 'change-history';
|
||||
result.details = delegatee && delegator ? `@${delegator} to @${delegatee}` : null;
|
||||
break;
|
||||
case 'cancel_transfer_from_savings':
|
||||
let { from: from_who, request_id: requestId } = opData;
|
||||
|
||||
result.value = `${0}`;
|
||||
result.icon = 'cancel';
|
||||
result.details = from_who ? `from @${from_who}, id: ${requestId}` : null;
|
||||
break;
|
||||
case 'fill_convert_request':
|
||||
let { owner: who, requestid: requestedId, amount_out: amount_out } = opData;
|
||||
|
||||
result.value = `${amount_out}`;
|
||||
result.icon = 'hourglass-full';
|
||||
result.details = who ? `@${who}, id: ${requestedId}` : null;
|
||||
break;
|
||||
case 'fill_transfer_from_savings':
|
||||
let { from: fillwho, to: fillto, amount: fillamount, request_id: fillrequestId } = opData;
|
||||
|
||||
result.value = `${fillamount}`;
|
||||
result.icon = 'hourglass-full';
|
||||
result.details = fillwho ? `@${fillwho} to @${fillto}, id: ${fillrequestId}` : null;
|
||||
break;
|
||||
case 'fill_vesting_withdraw':
|
||||
let { from_account: pd_who, to_account: pd_to, deposited: deposited } = opData;
|
||||
|
||||
result.value = `${deposited}`;
|
||||
result.icon = 'hourglass-full';
|
||||
result.details = pd_who ? `@${pd_who} to ${pd_to}` : null;
|
||||
break;
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export const groomingWalletData = async (user, globalProps, userCurrency) => {
|
||||
const walletData = {};
|
||||
|
||||
if (!user) {
|
||||
return walletData;
|
||||
}
|
||||
|
||||
const userdata = await getAccount(get(user, 'name'));
|
||||
|
||||
//const { accounts } = state;
|
||||
//if (!accounts) {
|
||||
// return walletData;
|
||||
//}
|
||||
|
||||
walletData.rewardHiveBalance = parseToken(userdata.reward_hive_balance);
|
||||
walletData.rewardHbdBalance = parseToken(userdata.reward_hbd_balance);
|
||||
walletData.rewardVestingHive = parseToken(userdata.reward_vesting_hive);
|
||||
walletData.hasUnclaimedRewards =
|
||||
walletData.rewardHiveBalance > 0 ||
|
||||
walletData.rewardHbdBalance > 0 ||
|
||||
walletData.rewardVestingHive > 0;
|
||||
walletData.balance = parseToken(userdata.balance);
|
||||
walletData.vestingShares = parseToken(userdata.vesting_shares);
|
||||
walletData.vestingSharesDelegated = parseToken(userdata.delegated_vesting_shares);
|
||||
walletData.vestingSharesReceived = parseToken(userdata.received_vesting_shares);
|
||||
walletData.vestingSharesTotal =
|
||||
walletData.vestingShares - walletData.vestingSharesDelegated + walletData.vestingSharesReceived;
|
||||
walletData.hbdBalance = parseToken(userdata.hbd_balance);
|
||||
walletData.savingBalance = parseToken(userdata.savings_balance);
|
||||
walletData.savingBalanceHbd = parseToken(userdata.savings_hbd_balance);
|
||||
|
||||
//TOOD: use base and quote from account.globalProps redux
|
||||
const feedHistory = await getFeedHistory();
|
||||
const base = parseToken(feedHistory.current_median_history.base);
|
||||
const quote = parseToken(feedHistory.current_median_history.quote);
|
||||
|
||||
walletData.hivePerMVests = globalProps.hivePerMVests;
|
||||
|
||||
const pricePerHive = base / quote;
|
||||
|
||||
const totalHive =
|
||||
vestsToHp(walletData.vestingShares, walletData.hivePerMVests) +
|
||||
walletData.balance +
|
||||
walletData.savingBalance;
|
||||
|
||||
const totalHbd = walletData.hbdBalance + walletData.savingBalanceHbd;
|
||||
|
||||
walletData.estimatedValue = totalHive * pricePerHive + totalHbd;
|
||||
|
||||
//TODO: cache data in redux or fetch once on wallet startup
|
||||
const ppHbd = await getCurrencyTokenRate(userCurrency, 'hbd');
|
||||
const ppHive = await getCurrencyTokenRate(userCurrency, 'hive');
|
||||
|
||||
walletData.estimatedHiveValue = (walletData.balance + walletData.savingBalance) * ppHive;
|
||||
walletData.estimatedHbdValue = totalHbd * ppHbd;
|
||||
walletData.estimatedHpValue =
|
||||
vestsToHp(walletData.vestingShares, walletData.hivePerMVests) * ppHive;
|
||||
|
||||
walletData.showPowerDown = userdata.next_vesting_withdrawal !== '1969-12-31T23:59:59';
|
||||
const timeDiff = Math.abs(parseDate(userdata.next_vesting_withdrawal) - new Date());
|
||||
walletData.nextVestingWithdrawal = Math.round(timeDiff / (1000 * 3600));
|
||||
|
||||
//TOOD: transfer history can be separated from here
|
||||
const op = utils.operationOrders
|
||||
const ops = [
|
||||
op.transfer, //HIVE
|
||||
op.author_reward, //HBD, HP
|
||||
op.curation_reward, //HP
|
||||
op.transfer_to_vesting, //HIVE, HP
|
||||
op.withdraw_vesting, //HIVE, HP
|
||||
op.interest, //HP
|
||||
op.transfer_to_savings, //HIVE, HBD
|
||||
op.transfer_from_savings, //HIVE, HBD
|
||||
op.fill_convert_request, //HBD
|
||||
op.fill_order, //HIVE, HBD
|
||||
op.claim_reward_balance, //HP
|
||||
op.sps_fund, //HBD
|
||||
op.comment_benefactor_reward, //HP
|
||||
op.return_vesting_delegation, //HP
|
||||
]
|
||||
|
||||
const history = await getAccountHistory(get(user, 'name'), ops);
|
||||
|
||||
const transfers = history.filter((tx) => transferTypes.includes(get(tx[1], 'op[0]', false)));
|
||||
|
||||
transfers.sort(compare);
|
||||
|
||||
walletData.transactions = transfers;
|
||||
return walletData;
|
||||
};
|
||||
|
||||
|
||||
|
||||
const fetchPendingRequests = async (username: string, coinSymbol: string): Promise<CoinActivity[]> => {
|
||||
|
||||
const _rawConversions = await getConversionRequests(username);
|
||||
const _rawOpenOrdres = await getOpenOrders(username);
|
||||
const _rawWithdrawRequests = await getSavingsWithdrawFrom(username);
|
||||
|
||||
console.log('fetched pending requests', _rawConversions, _rawOpenOrdres, _rawWithdrawRequests);
|
||||
|
||||
const openOrderRequests = _rawOpenOrdres
|
||||
.filter(request => request.sell_price.base.includes(coinSymbol))
|
||||
.map((request) => {
|
||||
const {base, quote} = request?.sell_price || {};
|
||||
return ({
|
||||
iconType: "MaterialIcons",
|
||||
textKey: 'open_order',
|
||||
expires: request.expiration,
|
||||
created: request.created,
|
||||
icon: 'reorder',
|
||||
value: base || '-- --',
|
||||
details: base && quote ? `@ ${base} = ${quote}`:'',
|
||||
} as CoinActivity)
|
||||
})
|
||||
|
||||
const withdrawRequests = _rawWithdrawRequests
|
||||
.filter(request => request.amount.includes(coinSymbol))
|
||||
.map((request) => {
|
||||
return ({
|
||||
iconType: "MaterialIcons",
|
||||
textKey: "withdraw_savings",
|
||||
created: request.complete,
|
||||
icon: "compare-arrows",
|
||||
value: request.amount,
|
||||
details: request.from && request.to ? `@${request.from} to @${request.to}` : null,
|
||||
memo: request.memo || null
|
||||
} as CoinActivity)
|
||||
})
|
||||
|
||||
const conversionRequests = _rawConversions
|
||||
.filter(request => request.amount.includes(coinSymbol))
|
||||
.map((request) => {
|
||||
return ({
|
||||
iconType: "MaterialIcons",
|
||||
textKey: "convert_request",
|
||||
created: request.conversion_date,
|
||||
icon: "hourglass-full",
|
||||
value: request.amount
|
||||
} as CoinActivity)
|
||||
})
|
||||
|
||||
const pendingRequests = [
|
||||
...openOrderRequests,
|
||||
...withdrawRequests,
|
||||
...conversionRequests
|
||||
];
|
||||
|
||||
pendingRequests.sort((a, b) => (
|
||||
new Date(a.expires || a.created).getTime() > new Date(b.expires || b.created).getTime() ? 1 : -1
|
||||
))
|
||||
|
||||
return pendingRequests;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param username
|
||||
* @param coinId
|
||||
* @param coinSymbol
|
||||
* @param globalProps
|
||||
* @returns {Promise<CoinActivitiesCollection>}
|
||||
*/
|
||||
export const fetchCoinActivities = async (username: string, coinId: string, coinSymbol: string, globalProps: GlobalProps): Promise<CoinActivitiesCollection> => {
|
||||
|
||||
const op = operationOrders;
|
||||
let history = [];
|
||||
|
||||
|
||||
|
||||
switch (coinId) {
|
||||
case COIN_IDS.ECENCY: {
|
||||
const pointActivities = await getUserPoints(username);
|
||||
console.log("Points Activities", pointActivities);
|
||||
const completed = pointActivities && pointActivities.length ?
|
||||
pointActivities.map((item) =>
|
||||
groomingPointsTransactionData({
|
||||
...item,
|
||||
icon: get(POINTS[get(item, 'type')], 'icon'),
|
||||
iconType: get(POINTS[get(item, 'type')], 'iconType'),
|
||||
textKey: get(POINTS[get(item, 'type')], 'textKey'),
|
||||
})
|
||||
) : [];
|
||||
return {
|
||||
completed,
|
||||
pending: [] as CoinActivity[]
|
||||
}
|
||||
}
|
||||
case COIN_IDS.HIVE:
|
||||
history = await getAccountHistory(username, [
|
||||
op.transfer, //HIVE
|
||||
op.transfer_to_vesting, //HIVE, HP
|
||||
op.withdraw_vesting, //HIVE, HP
|
||||
op.transfer_to_savings, //HIVE, HBD
|
||||
op.transfer_from_savings, //HIVE, HBD
|
||||
op.fill_order, //HIVE, HBD
|
||||
]);
|
||||
break;
|
||||
case COIN_IDS.HBD:
|
||||
history = await getAccountHistory(username, [
|
||||
op.transfer, //HIVE //HBD
|
||||
op.author_reward, //HBD, HP
|
||||
op.transfer_to_savings, //HIVE, HBD
|
||||
op.transfer_from_savings, //HIVE, HBD
|
||||
op.fill_convert_request, //HBD
|
||||
op.fill_order, //HIVE, HBD
|
||||
op.sps_fund, //HBD
|
||||
]);
|
||||
break;
|
||||
case COIN_IDS.HP:
|
||||
history = await getAccountHistory(username, [
|
||||
op.author_reward, //HBD, HP
|
||||
op.curation_reward, //HP
|
||||
op.transfer_to_vesting, //HIVE, HP
|
||||
op.withdraw_vesting, //HIVE, HP
|
||||
op.interest, //HP
|
||||
op.claim_reward_balance, //HP
|
||||
op.comment_benefactor_reward, //HP
|
||||
op.return_vesting_delegation, //HP
|
||||
]);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
const transfers = history.filter((tx) => transferTypes.includes(get(tx[1], 'op[0]', false)));
|
||||
transfers.sort(compare);
|
||||
|
||||
const activities = transfers.map(item => groomingTransactionData(item, globalProps.hivePerMVests));
|
||||
const filterdActivities: CoinActivity[] = activities
|
||||
? activities.filter((item) => {
|
||||
return (
|
||||
item &&
|
||||
item.value &&
|
||||
item.value.includes(coinSymbol)
|
||||
);
|
||||
})
|
||||
: [];
|
||||
|
||||
console.log('FILTERED comap', activities.length, filterdActivities.length)
|
||||
|
||||
const pendingRequests = await fetchPendingRequests(username, coinSymbol);
|
||||
return {
|
||||
completed: filterdActivities,
|
||||
pending: pendingRequests,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const calculateConvertingAmount = (requests: ConversionRequest[]): number => {
|
||||
if (!requests || !requests.length) {
|
||||
return 0;
|
||||
}
|
||||
//TODO: add method body
|
||||
// ecency-vision -> src/common/components/wallet-hive/index.tsx#fetchConvertingAmount
|
||||
throw new Error("calculateConvertingAmount method body not implemented yet");
|
||||
}
|
||||
|
||||
const calculateSavingsWithdrawalAmount = (requests: SavingsWithdrawRequest[], coinSymbol: string): number => {
|
||||
return requests.reduce((prevVal, curRequest) => {
|
||||
const _amount = curRequest.amount;
|
||||
return _amount.includes(coinSymbol)
|
||||
? prevVal + parseAsset(_amount).amount
|
||||
: prevVal
|
||||
}, 0);
|
||||
}
|
||||
|
||||
const calculateOpenOrdersAmount = (requests: OpenOrderItem[], coinSymbol: string): number => {
|
||||
return requests.reduce((prevVal, curRequest) => {
|
||||
const _basePrice = curRequest.sell_price.base;
|
||||
return _basePrice.includes(coinSymbol)
|
||||
? prevVal + parseAsset(_basePrice).amount
|
||||
: prevVal
|
||||
}, 0);
|
||||
}
|
||||
|
||||
|
||||
export const fetchCoinsData = async ({
|
||||
coins,
|
||||
currentAccount,
|
||||
vsCurrency,
|
||||
currencyRate,
|
||||
globalProps,
|
||||
refresh,
|
||||
quotes,
|
||||
}: {
|
||||
coins: CoinBase[],
|
||||
currentAccount: any,
|
||||
vsCurrency: string,
|
||||
currencyRate: number,
|
||||
globalProps: GlobalProps,
|
||||
quotes: { [key: string]: QuoteItem }
|
||||
refresh: boolean,
|
||||
})
|
||||
: Promise<{ [key: string]: CoinData }> => {
|
||||
|
||||
const username = currentAccount.username;
|
||||
const { base, quote, hivePerMVests } = globalProps
|
||||
|
||||
const coinData = {} as { [key: string]: CoinData };
|
||||
const walletData = {} as any;
|
||||
|
||||
|
||||
if (!username) {
|
||||
return walletData;
|
||||
}
|
||||
|
||||
//TODO: Use already available accoutn for frist wallet start
|
||||
const userdata = refresh ? await getAccount(username) : currentAccount;
|
||||
const _ecencyUserData = refresh ? await getEcencyUser(username) : currentAccount.ecencyUserData
|
||||
//TODO: cache data in redux or fetch once on wallet startup
|
||||
const _prices = !refresh && quotes ? quotes : await getLatestQuotes(currencyRate); //TODO: figure out a way to handle other currencies
|
||||
|
||||
|
||||
coins.forEach((coinBase) => {
|
||||
|
||||
switch (coinBase.id) {
|
||||
case COIN_IDS.ECENCY: {
|
||||
const balance = _ecencyUserData.points ? parseFloat(_ecencyUserData.points) : 0;
|
||||
const unclaimedFloat = parseFloat(_ecencyUserData.unclaimed_points || '0');
|
||||
const unclaimedBalance = unclaimedFloat ? unclaimedFloat + ' Points' : '';
|
||||
const ppEstm = _prices[coinBase.id].price;
|
||||
|
||||
coinData[coinBase.id] = {
|
||||
balance: Math.round(balance * 1000) / 1000,
|
||||
estimateValue: balance * ppEstm,
|
||||
vsCurrency: vsCurrency,
|
||||
currentPrice: ppEstm,
|
||||
unclaimedBalance: unclaimedBalance,
|
||||
actions: ECENCY_ACTIONS,
|
||||
}
|
||||
break;
|
||||
}
|
||||
case COIN_IDS.HIVE: {
|
||||
const balance = parseToken(userdata.balance);
|
||||
const savings = parseToken(userdata.savings_balance);
|
||||
const ppHive = _prices[coinBase.id].price;
|
||||
|
||||
|
||||
coinData[coinBase.id] = {
|
||||
balance: Math.round(balance * 1000) / 1000,
|
||||
estimateValue: (balance + savings) * ppHive,
|
||||
savings: Math.round(savings * 1000) / 1000,
|
||||
vsCurrency: vsCurrency,
|
||||
currentPrice: ppHive,
|
||||
unclaimedBalance: '',
|
||||
actions: HIVE_ACTIONS,
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case COIN_IDS.HBD: {
|
||||
const balance = parseToken(userdata.hbd_balance);
|
||||
const savings = parseToken(userdata.savings_hbd_balance);
|
||||
const ppHbd = _prices[coinBase.id].price;
|
||||
|
||||
coinData[coinBase.id] = {
|
||||
balance: Math.round(balance * 1000) / 1000,
|
||||
estimateValue: (balance + savings) * ppHbd,
|
||||
savings: Math.round(savings * 1000) / 1000,
|
||||
vsCurrency: vsCurrency,
|
||||
currentPrice: ppHbd,
|
||||
unclaimedBalance: '',
|
||||
actions: HBD_ACTIONS,
|
||||
}
|
||||
break;
|
||||
}
|
||||
case COIN_IDS.HP: {
|
||||
const _getBalanceStr = (val: number, cur: string) => (val ? Math.round(val * 1000) / 1000 + cur : '');
|
||||
const balance = Math.round(
|
||||
vestsToHp(parseToken(userdata.vesting_shares), hivePerMVests) * 1000,
|
||||
) / 1000;
|
||||
|
||||
const receivedHP = vestsToHp(
|
||||
parseToken(userdata.received_vesting_shares),
|
||||
hivePerMVests,
|
||||
)
|
||||
|
||||
const delegatedHP = vestsToHp(
|
||||
parseToken(userdata.delegated_vesting_shares),
|
||||
hivePerMVests,
|
||||
)
|
||||
|
||||
//agggregate claim button text
|
||||
const unclaimedBalance = [
|
||||
_getBalanceStr(parseToken(userdata.reward_hive_balance), ' HIVE'),
|
||||
_getBalanceStr(parseToken(userdata.reward_hbd_balance), ' HBD'),
|
||||
_getBalanceStr(parseToken(userdata.reward_vesting_hive), ' HP')
|
||||
].reduce(
|
||||
(prevVal, bal) => prevVal + (!bal ? '' : (`${prevVal !== '' ? ' ' : ''}${bal}`)),
|
||||
''
|
||||
);
|
||||
|
||||
//calculate power down
|
||||
const isPoweringDown = userdata.next_vesting_withdrawal
|
||||
? parseDate(userdata.next_vesting_withdrawal) > new Date()
|
||||
: false;
|
||||
|
||||
const nextVestingSharesWithdrawal = isPoweringDown
|
||||
? Math.min(
|
||||
parseAsset(userdata.vesting_withdraw_rate).amount,
|
||||
(Number(userdata.to_withdraw) - Number(userdata.withdrawn)) / 1e6
|
||||
) : 0;
|
||||
const nextVestingSharesWithdrawalHive = isPoweringDown ? vestsToHp(nextVestingSharesWithdrawal, hivePerMVests) : 0;
|
||||
|
||||
//TODO: assess how we can make this value change live.
|
||||
const estimateVoteValueStr = '$ ' + getEstimatedAmount(userdata, globalProps);
|
||||
|
||||
const ppHive = _prices[COIN_IDS.HIVE].price;
|
||||
coinData[coinBase.id] = {
|
||||
balance: Math.round(balance * 1000) / 1000,
|
||||
estimateValue: balance * ppHive,
|
||||
unclaimedBalance,
|
||||
vsCurrency: vsCurrency,
|
||||
currentPrice: ppHive,
|
||||
actions: HIVE_POWER_ACTIONS,
|
||||
extraDataPairs: [
|
||||
{
|
||||
labelId: 'delegated_hive_power',
|
||||
value: `- ${delegatedHP.toFixed(3)} HP`
|
||||
}, {
|
||||
labelId: 'received_hive_power',
|
||||
value: `+ ${receivedHP.toFixed(3)} HP`
|
||||
}, {
|
||||
labelId: 'powering_down_hive_power',
|
||||
value: `- ${nextVestingSharesWithdrawalHive.toFixed(3)} HP`
|
||||
}, {
|
||||
labelId: 'total_hive_power',
|
||||
value: `${(balance - delegatedHP + receivedHP - nextVestingSharesWithdrawalHive).toFixed(3)} HP`
|
||||
}, {
|
||||
labelId: 'vote_value',
|
||||
value: estimateVoteValueStr
|
||||
}
|
||||
]
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
})
|
||||
|
||||
//TODO:discard unnessacry data processings towards the end of PR
|
||||
walletData.rewardHiveBalance = parseToken(userdata.reward_hive_balance);
|
||||
walletData.rewardHbdBalance = parseToken(userdata.reward_hbd_balance);
|
||||
walletData.rewardVestingHive = parseToken(userdata.reward_vesting_hive);
|
||||
|
||||
walletData.hasUnclaimedRewards =
|
||||
walletData.rewardHiveBalance > 0 ||
|
||||
walletData.rewardHbdBalance > 0 ||
|
||||
walletData.rewardVestingHive > 0;
|
||||
|
||||
walletData.balance = parseToken(userdata.balance);
|
||||
walletData.vestingShares = parseToken(userdata.vesting_shares);
|
||||
walletData.vestingSharesDelegated = parseToken(userdata.delegated_vesting_shares);
|
||||
walletData.vestingSharesReceived = parseToken(userdata.received_vesting_shares);
|
||||
walletData.vestingSharesTotal =
|
||||
walletData.vestingShares - walletData.vestingSharesDelegated + walletData.vestingSharesReceived;
|
||||
walletData.hbdBalance = parseToken(userdata.hbd_balance);
|
||||
walletData.savingBalance = parseToken(userdata.savings_balance);
|
||||
walletData.savingBalanceHbd = parseToken(userdata.savings_hbd_balance);
|
||||
|
||||
|
||||
|
||||
walletData.hivePerMVests = hivePerMVests;
|
||||
const pricePerHive = base / quote;
|
||||
|
||||
const totalHive =
|
||||
vestsToHp(walletData.vestingShares, walletData.hivePerMVests) +
|
||||
walletData.balance +
|
||||
walletData.savingBalance;
|
||||
|
||||
const totalHbd = walletData.hbdBalance + walletData.savingBalanceHbd;
|
||||
|
||||
walletData.estimatedValue = totalHive * pricePerHive + totalHbd;
|
||||
|
||||
|
||||
|
||||
walletData.showPowerDown = userdata.next_vesting_withdrawal !== '1969-12-31T23:59:59';
|
||||
const timeDiff = Math.abs(parseDate(userdata.next_vesting_withdrawal) - new Date());
|
||||
walletData.nextVestingWithdrawal = Math.round(timeDiff / (1000 * 3600));
|
||||
|
||||
|
||||
return coinData;
|
||||
}
|
||||
|
||||
function compare(a, b) {
|
||||
if (a[1].block < b[1].block) {
|
||||
return 1;
|
||||
}
|
||||
if (a[1].block > b[1].block) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export const groomingPointsTransactionData = (transaction) => {
|
||||
if (!transaction) {
|
||||
return null;
|
||||
}
|
||||
const result = {
|
||||
...transaction,
|
||||
};
|
||||
|
||||
result.details = get(transaction, 'sender')
|
||||
? `from @${get(transaction, 'sender')}`
|
||||
: get(transaction, 'receiver') && `to @${get(transaction, 'receiver')}`;
|
||||
|
||||
result.value = `${get(transaction, 'amount')} Points`;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const getPointsEstimate = async (amount, userCurrency) => {
|
||||
if (!amount) {
|
||||
return 0;
|
||||
}
|
||||
const ppEstm = await getCurrencyTokenRate(userCurrency, 'estm');
|
||||
|
||||
return ppEstm * amount;
|
||||
};
|
||||
|
||||
export const getBtcEstimate = async (amount, userCurrency) => {
|
||||
if (!amount) {
|
||||
return 0;
|
||||
}
|
||||
const ppBtc = await getCurrencyTokenRate(userCurrency, 'btc');
|
||||
|
||||
return ppBtc * amount;
|
||||
};
|
25
yarn.lock
25
yarn.lock
@ -8088,6 +8088,11 @@ path@^0.12.7:
|
||||
process "^0.11.1"
|
||||
util "^0.10.3"
|
||||
|
||||
paths-js@^0.4.10:
|
||||
version "0.4.11"
|
||||
resolved "https://registry.yarnpkg.com/paths-js/-/paths-js-0.4.11.tgz#b2a9d5f94ee9949aa8fee945f78a12abff44599e"
|
||||
integrity sha512-3mqcLomDBXOo7Fo+UlaenG6f71bk1ZezPQy2JCmYHy2W2k5VKpP+Jbin9H0bjXynelTbglCqdFhSEkeIkKTYUA==
|
||||
|
||||
pbkdf2@3.0.8:
|
||||
version "3.0.8"
|
||||
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.8.tgz#2f8abf16ebecc82277945d748aba1d78761f61e2"
|
||||
@ -8201,6 +8206,11 @@ pngjs@^3.3.0:
|
||||
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f"
|
||||
integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==
|
||||
|
||||
point-in-polygon@^1.0.1:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/point-in-polygon/-/point-in-polygon-1.1.0.tgz#b0af2616c01bdee341cbf2894df643387ca03357"
|
||||
integrity sha512-3ojrFwjnnw8Q9242TzgXuTD+eKiutbzyslcq1ydfu82Db2y+Ogbmyrkpv0Hgj31qwT3lbS9+QAAO/pIQM35XRw==
|
||||
|
||||
polygoat@1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/polygoat/-/polygoat-1.1.4.tgz#329f9a0d1b2d4a45149e2539523c6f7dfd850a5f"
|
||||
@ -8555,6 +8565,15 @@ react-native-camera@^4.2.1:
|
||||
dependencies:
|
||||
prop-types "^15.6.2"
|
||||
|
||||
react-native-chart-kit@^6.11.0:
|
||||
version "6.11.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native-chart-kit/-/react-native-chart-kit-6.11.0.tgz#513e0944cd8f946e1827facc17fe766ae487d91b"
|
||||
integrity sha512-mRSfW+mcyVSi0UZKFucru5h5TPB6UcWrMi/DphICRpPJWlvIVAv/6VYGFA57NFDLoRVufFjbsRRrms+TOq61jw==
|
||||
dependencies:
|
||||
lodash "^4.17.13"
|
||||
paths-js "^0.4.10"
|
||||
point-in-polygon "^1.0.1"
|
||||
|
||||
react-native-config@luggit/react-native-config#master:
|
||||
version "1.4.2"
|
||||
resolved "https://codeload.github.com/luggit/react-native-config/tar.gz/81f599f5f912b84c41c9ef2901faf54995638c4e"
|
||||
@ -10690,9 +10709,9 @@ uri-js@^4.2.2:
|
||||
punycode "^2.1.0"
|
||||
|
||||
urijs@^1.19.6:
|
||||
version "1.19.8"
|
||||
resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.8.tgz#ee0407a18528934d3c383e691912f47043a58feb"
|
||||
integrity sha512-iIXHrjomQ0ZCuDRy44wRbyTZVnfVNLVo3Ksz1yxNyE5wV1IDZW2S5Jszy45DTlw/UdsnRT7DyDhIz7Gy+vJumw==
|
||||
version "1.19.10"
|
||||
resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.10.tgz#8e2fe70a8192845c180f75074884278f1eea26cb"
|
||||
integrity sha512-EzauQlgKuJgsXOqoMrCiePBf4At5jVqRhXykF3Wfb8ZsOBMxPcfiVBcsHXug4Aepb/ICm2PIgqAUGMelgdrWEg==
|
||||
|
||||
urix@^0.1.0:
|
||||
version "0.1.0"
|
||||
|
Loading…
Reference in New Issue
Block a user