From 4c571b5be9949e162ad16cf0a9eecc5c403ea9df Mon Sep 17 00:00:00 2001 From: gamingumar Date: Tue, 26 Sep 2023 22:39:43 +0500 Subject: [PATCH 1/8] feat: GU-2746 add recurrent transfer option in UI, api call added --- reactotron-config.ts | 5 +++ .../transferAmountInputSection.tsx | 23 +++++++++++ src/config/locales/en-US.json | 4 ++ src/constants/transferTypes.ts | 1 + src/containers/transferContainer.js | 13 ++++++- src/providers/hive/dhive.js | 39 +++++++++++++++++++ src/redux/reducers/walletReducer.ts | 1 + .../screen/assetDetailsScreen.tsx | 1 + src/screens/transfer/index.js | 2 + src/screens/transfer/screen/transferScreen.js | 21 ++++++++-- src/utils/wallet.ts | 3 +- 11 files changed, 108 insertions(+), 5 deletions(-) diff --git a/reactotron-config.ts b/reactotron-config.ts index af3d8a088..5f78c3acc 100644 --- a/reactotron-config.ts +++ b/reactotron-config.ts @@ -15,3 +15,8 @@ const reactotron = Reactotron.setAsyncStorageHandler(AsyncStorage) // AsyncStora .connect(); // let's connect! export default reactotron; + +export const log = (...rest) => { + Reactotron.log(...rest); + console.log(...rest); +}; diff --git a/src/components/transferAmountInputSection/transferAmountInputSection.tsx b/src/components/transferAmountInputSection/transferAmountInputSection.tsx index 0f6d3cc14..fa5b6f6c1 100644 --- a/src/components/transferAmountInputSection/transferAmountInputSection.tsx +++ b/src/components/transferAmountInputSection/transferAmountInputSection.tsx @@ -18,6 +18,8 @@ export interface TransferAmountInputSectionProps { setMemo: (value: string) => void; amount: string; setAmount: (value: string) => void; + recurrence: string; + setRecurrence: (value: string) => void; hsTransfer: boolean; transferType: string; selectedAccount: any; @@ -36,6 +38,8 @@ const TransferAmountInputSection = ({ setMemo, amount, setAmount, + recurrence, + setRecurrence, transferType, fundType, disableMinimum, @@ -63,6 +67,9 @@ const TransferAmountInputSection = ({ if (state === 'memo') { setMemo(_amount); } + if (state === 'recurrence') { + setRecurrence(val); + } }; const _renderInput = (placeholder, state, keyboardType, isTextArea) => ( @@ -76,6 +83,8 @@ const TransferAmountInputSection = ({ ? amount : state === 'memo' ? memo + : state === 'recurrence' + ? recurrence : '' } placeholder={placeholder} @@ -124,8 +133,22 @@ const TransferAmountInputSection = ({ )} /> + {transferType === TransferTypes.RECURRENT_TRANSFER && ( + + _renderInput( + intl.formatMessage({ id: 'transfer.recurrence_placeholder' }), + 'recurrence', + 'numeric', + false, + ) + } + /> + )} {(transferType === TransferTypes.POINTS || transferType === TransferTypes.TRANSFER_TOKEN || + transferType === TransferTypes.RECURRENT_TRANSFER || transferType === TransferTypes.TRANSFER_TO_SAVINGS || transferType === TransferTypes.TRANSFER_ENGINE || transferType === TransferTypes.TRANSFER_SPK || diff --git a/src/config/locales/en-US.json b/src/config/locales/en-US.json index 46dfc6911..9626499b1 100644 --- a/src/config/locales/en-US.json +++ b/src/config/locales/en-US.json @@ -7,6 +7,7 @@ "claim_reward_balance": "Claim Reward ", "points_activity":"Activity", "transfer": "Transfer", + "recurrent_transfer": "Recurrent Transfer", "power_up": "To Vesting", "transfer_from_savings": "From Savings", "withdraw_savings":"Withdraw Savings", @@ -770,13 +771,16 @@ "to": "To", "amount_information": "Drag the slider to adjust the amount", "amount": "Amount", + "recurrence": "Recurrence", "memo": "Memo", + "recurrent_transfer": "Recurrent Transfer", "information": "Continue transaction?", "amount_desc": "Balance", "memo_desc": "This memo is public", "convert_desc": "Convert takes 3.5 days and NOT recommended IF HBD price is higher than $1", "to_placeholder": "Username", "memo_placeholder": "Enter your notes here", + "recurrence_placeholder": "Recurrence for Months", "transfer_token": "Transfer", "purchase_estm": "Purchase Points", "convert": "Convert HBD to HIVE", diff --git a/src/constants/transferTypes.ts b/src/constants/transferTypes.ts index e94545ec6..5adf93695 100644 --- a/src/constants/transferTypes.ts +++ b/src/constants/transferTypes.ts @@ -12,6 +12,7 @@ const TransferTypes = { POWER_DOWN: 'power_down', ADDRESS_VIEW: 'address_view', DELEGATE_VESTING_SHARES: 'delegate_vesting_shares', + RECURRENT_TRANSFER: 'recurrent_transfer', // Engine Transfer types WITHDRAW_VESTING: 'withdraw_vesting', diff --git a/src/containers/transferContainer.js b/src/containers/transferContainer.js index e6fa28aff..b5bd42f40 100644 --- a/src/containers/transferContainer.js +++ b/src/containers/transferContainer.js @@ -16,6 +16,7 @@ import { withdrawVesting, delegateVestingShares, setWithdrawVestingRoute, + recurrentTransferToken, } from '../providers/hive/dhive'; import { toastNotification } from '../redux/actions/uiAction'; import { getUserDataWithUsername } from '../realm/realm'; @@ -63,6 +64,7 @@ class TransferContainer extends Component { spkMarkets: [], initialAmount: props.route.params?.initialAmount, initialMemo: props.route.params?.initialMemo, + initialRecurrence: props.route.params?.initialRecurrence, }; } @@ -188,7 +190,7 @@ class TransferContainer extends Component { }, 3000); }; - _transferToAccount = async (from, destination, amount, memo) => { + _transferToAccount = async (from, destination, amount, memo, recurrence = null) => { const { pinCode, navigation, dispatch, intl, route } = this.props; let { currentAccount } = this.props; const { selectedAccount } = this.state; @@ -205,6 +207,10 @@ class TransferContainer extends Component { fundType, }; + if (recurrence) { + data.recurrence = +recurrence; + } + if (countDecimals(Number(data.amount)) < 3) { data.amount = Number(data.amount).toFixed(3); } @@ -214,6 +220,9 @@ class TransferContainer extends Component { case 'transfer_token': func = transferToken; break; + case TransferTypes.RECURRENT_TRANSFER: + func = recurrentTransferToken; + break; case 'purchase_estm': func = transferToken; break; @@ -344,6 +353,7 @@ class TransferContainer extends Component { spkMarkets, initialAmount, initialMemo, + initialRecurrence, } = this.state; const transferType = route.params?.transferType ?? ''; @@ -371,6 +381,7 @@ class TransferContainer extends Component { setWithdrawVestingRoute: this._setWithdrawVestingRoute, initialAmount, initialMemo, + initialRecurrence, }) ); } diff --git a/src/providers/hive/dhive.js b/src/providers/hive/dhive.js index 17c6a4f19..6b1bbe7d4 100644 --- a/src/providers/hive/dhive.js +++ b/src/providers/hive/dhive.js @@ -43,6 +43,7 @@ import { b64uEnc } from '../../utils/b64'; import bugsnagInstance from '../../config/bugsnag'; import bugsnapInstance from '../../config/bugsnag'; import { makeJsonMetadataReply } from '../../utils/editor'; +import TransferTypes from '../../constants/transferTypes'; const hiveuri = require('hive-uri'); global.Buffer = global.Buffer || require('buffer').Buffer; @@ -987,6 +988,44 @@ export const transferToken = (currentAccount, pin, data) => { ); }; +export const recurrentTransferToken = (currentAccount, pin, data) => { + const digitPinCode = getDigitPinCode(pin); + const key = getAnyPrivateKey( + { + activeKey: get(currentAccount, 'local.activeKey'), + }, + digitPinCode, + ); + + if (key) { + const privateKey = PrivateKey.fromString(key); + const args = { + from: get(data, 'from'), + to: get(data, 'destination'), + amount: get(data, 'amount'), + memo: get(data, 'memo'), + recurrence: get(data, 'recurrence'), + }; + const opArray = [[TransferTypes.RECURRENT_TRANSFER, args]]; + + return new Promise((resolve, reject) => { + sendHiveOperations(opArray, privateKey) + .then((result) => { + if (result) { + resolve(result); + } + }) + .catch((err) => { + reject(err); + }); + }); + } + + return Promise.reject( + new Error('Check private key permission! Required private active key or above.'), + ); +}; + export const convert = (currentAccount, pin, data) => { const digitPinCode = getDigitPinCode(pin); const key = getAnyPrivateKey( diff --git a/src/redux/reducers/walletReducer.ts b/src/redux/reducers/walletReducer.ts index 1052aeb27..7cbe28214 100644 --- a/src/redux/reducers/walletReducer.ts +++ b/src/redux/reducers/walletReducer.ts @@ -59,6 +59,7 @@ export interface CoinActivity { details: string | null; memo: string; cancelable: boolean; + recurrence: string; } export interface QuoteItem { diff --git a/src/screens/assetDetails/screen/assetDetailsScreen.tsx b/src/screens/assetDetails/screen/assetDetailsScreen.tsx index 058c631eb..63fa66b6a 100644 --- a/src/screens/assetDetails/screen/assetDetailsScreen.tsx +++ b/src/screens/assetDetails/screen/assetDetailsScreen.tsx @@ -156,6 +156,7 @@ const AssetDetailsScreen = ({ navigation, route }: AssetDetailsScreenProps) => { referredUsername: baseActivity.details?.split(' ')[2]?.slice(1), // from @user1 to @user2 initialAmount: `${parseToken(baseActivity.value)}`, initialMemo: baseActivity.memo, + initialRecurrence: `${parseToken(baseActivity.recurrence)}`, }; } diff --git a/src/screens/transfer/index.js b/src/screens/transfer/index.js index bd8af515a..bc7c3c16d 100644 --- a/src/screens/transfer/index.js +++ b/src/screens/transfer/index.js @@ -30,6 +30,7 @@ const Transfer = ({ navigation, route }) => ( spkMarkets, initialAmount, initialMemo, + initialRecurrence, }) => { switch (transferType) { case 'delegate': @@ -98,6 +99,7 @@ const Transfer = ({ navigation, route }) => ( referredUsername={referredUsername || ''} initialAmount={initialAmount || ''} initialMemo={initialMemo || ''} + initialRecurrence={initialRecurrence || ''} /> ); } diff --git a/src/screens/transfer/screen/transferScreen.js b/src/screens/transfer/screen/transferScreen.js index bec8e540e..006e3285e 100644 --- a/src/screens/transfer/screen/transferScreen.js +++ b/src/screens/transfer/screen/transferScreen.js @@ -44,7 +44,7 @@ const TransferView = ({ referredUsername, initialAmount, initialMemo, - transactionData, + initialRecurrence, }) => { const dispatch = useAppDispatch(); @@ -75,6 +75,8 @@ const TransferView = ({ const [memo, setMemo] = useState( transferType === 'purchase_estm' ? 'estm-purchase' : initialMemo, ); + const [recurrence, setRecurrence] = useState(`${initialRecurrence}`); + const [isUsernameValid, setIsUsernameValid] = useState( !!( transferType === 'purchase_estm' || @@ -105,7 +107,13 @@ const TransferView = ({ if (accountType === AUTH_TYPE.STEEM_CONNECT) { setHsTransfer(true); } else { - transferToAccount(from, destination, amount, memo); + transferToAccount( + from, + destination, + amount, + memo, + transferType === TransferTypes.RECURRENT_TRANSFER ? recurrence : null, + ); } }, 300, @@ -133,7 +141,11 @@ const TransferView = ({ // )}`; // } else - if (transferType === TransferTypes.TRANSFER_TO_SAVINGS) { + if (transferType === TransferTypes.RECURRENT_TRANSFER) { + path = `sign/recurrent_transfer?from=${currentAccountName}&to=${destination}&amount=${encodeURIComponent( + `${amount} ${fundType}`, + )}&memo=${encodeURIComponent(memo)}&recurrence=${recurrence}`; + } else if (transferType === TransferTypes.TRANSFER_TO_SAVINGS) { path = `sign/transfer_to_savings?from=${currentAccountName}&to=${destination}&amount=${encodeURIComponent( `${amount} ${fundType}`, )}&memo=${encodeURIComponent(memo)}`; @@ -200,6 +212,7 @@ const TransferView = ({ `${amount} ${fundType}`, )}&memo=${encodeURIComponent(memo)}`; } + console.log('path is: ', path); } const _onNextPress = () => { @@ -268,6 +281,8 @@ const TransferView = ({ setMemo={setMemo} amount={amount} setAmount={setAmount} + recurrence={recurrence} + setRecurrence={setRecurrence} hsTransfer={hsTransfer} transferType={transferType} selectedAccount={selectedAccount} diff --git a/src/utils/wallet.ts b/src/utils/wallet.ts index efc10ec72..c079da6d2 100644 --- a/src/utils/wallet.ts +++ b/src/utils/wallet.ts @@ -68,7 +68,8 @@ const HIVE_ACTIONS = [ 'transfer_to_savings', 'transfer_to_vesting', 'withdraw_hive', - 'swap_token' + 'swap_token', + TransferTypes.RECURRENT_TRANSFER ]; const HBD_ACTIONS = [ 'transfer_token', From ed7c60eeabe06fd690757e47c1c80681d99881bb Mon Sep 17 00:00:00 2001 From: gamingumar Date: Thu, 5 Oct 2023 10:02:14 +0500 Subject: [PATCH 2/8] fix: GU-2746 resolve iOS 17 and xcode 15 crash --- ios/Podfile.lock | 4 ++-- package.json | 7 +------ .../transferAccountSelector/transferAccountSelector.tsx | 1 + yarn.lock | 8 ++++---- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index d26aee693..cf386586b 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -20,7 +20,7 @@ PODS: - boost (1.76.0) - BugsnagReactNative (7.19.0): - React-Core - - BVLinearGradient (2.6.2): + - BVLinearGradient (2.8.3): - React-Core - CocoaAsyncSocket (7.6.5) - DoubleConversion (1.1.6) @@ -998,7 +998,7 @@ SPEC CHECKSUMS: AppCenterReactNativeShared: f395caeabde0dc3a11609dbcb737d0f14cd40e79 boost: a7c83b31436843459a1961bfd74b96033dc77234 BugsnagReactNative: fa312f53a83ca21c0afdfeaec98a9c5eeb8fc4ed - BVLinearGradient: 34a999fda29036898a09c6a6b728b0b4189e1a44 + BVLinearGradient: 880f91a7854faff2df62518f0281afb1c60d49a3 CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 FBLazyVector: a6454570f573a0f6f1d397e5a95c13e8e45d1700 diff --git a/package.json b/package.json index 4e75ab1ff..65999ff2f 100644 --- a/package.json +++ b/package.json @@ -126,7 +126,7 @@ "react-native-iphone-x-helper": "Norcy/react-native-iphone-x-helper", "react-native-keyboard-aware-scroll-view": "^0.9.1", "react-native-level-fs": "^3.0.0", - "react-native-linear-gradient": "^2.4.2", + "react-native-linear-gradient": "^2.8.3", "react-native-media-controls": "^2.3.0", "react-native-modal": "13.0.1", "react-native-modal-dropdown": "^1.0.2", @@ -220,11 +220,6 @@ "git add" ] }, - "husky": { - "hooks": { - "pre-commit": "lint-staged" - } - }, "jest": { "preset": "react-native" }, diff --git a/src/components/transferAccountSelector/transferAccountSelector.tsx b/src/components/transferAccountSelector/transferAccountSelector.tsx index 57ea864c1..7cee4ec56 100644 --- a/src/components/transferAccountSelector/transferAccountSelector.tsx +++ b/src/components/transferAccountSelector/transferAccountSelector.tsx @@ -82,6 +82,7 @@ const TransferAccountSelector = ({ setDestination(username); }; + //todo: fetch existing recurrent transfer const _debouncedValidateUsername = useCallback( debounce((username: string) => { getAccountsWithUsername(username).then((res) => { diff --git a/yarn.lock b/yarn.lock index 3646d678d..2afb5dbca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9131,10 +9131,10 @@ react-native-level-fs@^3.0.0: level-filesystem "^1.0.1" levelup "^0.18.2" -react-native-linear-gradient@^2.4.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/react-native-linear-gradient/-/react-native-linear-gradient-2.6.2.tgz#56598a76832724b2afa7889747635b5c80948f38" - integrity sha512-Z8Xxvupsex+9BBFoSYS87bilNPWcRfRsGC0cpJk72Nxb5p2nEkGSBv73xZbEHnW2mUFvP+huYxrVvjZkr/gRjQ== +react-native-linear-gradient@^2.8.3: + version "2.8.3" + resolved "https://registry.yarnpkg.com/react-native-linear-gradient/-/react-native-linear-gradient-2.8.3.tgz#9a116649f86d74747304ee13db325e20b21e564f" + integrity sha512-KflAXZcEg54PXkLyflaSZQ3PJp4uC4whM7nT/Uot9m0e/qxFV3p6uor1983D1YOBJbJN7rrWdqIjq0T42jOJyA== react-native-media-controls@^2.3.0: version "2.3.0" From 04fc0b7a50c5df4b815c8c97d70b33c8efd86bc6 Mon Sep 17 00:00:00 2001 From: gamingumar Date: Wed, 25 Oct 2023 10:12:15 +0500 Subject: [PATCH 3/8] fix: GU-2746 add support for reurrent transfers, fetch existing recurrent transfer in progress --- ios/Podfile.lock | 2 +- .../settingsItem/view/settingsItemView.js | 2 +- .../transferAccountSelector.tsx | 16 +++ .../transferAmountInputSection.tsx | 123 ++++++++++++++---- .../transferAmountInputSectionStyles.ts | 24 ++++ src/config/locales/en-US.json | 4 +- src/containers/transferContainer.js | 44 ++++++- src/providers/hive/dhive.js | 17 ++- src/redux/reducers/walletReducer.ts | 1 + .../screen/assetDetailsScreen.tsx | 2 + src/screens/transfer/index.js | 6 + src/screens/transfer/screen/transferScreen.js | 57 +++++++- 12 files changed, 260 insertions(+), 38 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index cf386586b..d56f1933b 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1112,4 +1112,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 6b212d63236c21489948e9b73b59fcff2feeeb08 -COCOAPODS: 1.11.3 +COCOAPODS: 1.13.0 diff --git a/src/components/settingsItem/view/settingsItemView.js b/src/components/settingsItem/view/settingsItemView.js index eaae06a96..e13946000 100644 --- a/src/components/settingsItem/view/settingsItemView.js +++ b/src/components/settingsItem/view/settingsItemView.js @@ -107,7 +107,7 @@ class SettingsItemView extends PureComponent { return ( - {title} + {!!title && {title}} {this._renderItem()} ); diff --git a/src/components/transferAccountSelector/transferAccountSelector.tsx b/src/components/transferAccountSelector/transferAccountSelector.tsx index 7cee4ec56..d48469574 100644 --- a/src/components/transferAccountSelector/transferAccountSelector.tsx +++ b/src/components/transferAccountSelector/transferAccountSelector.tsx @@ -31,6 +31,7 @@ export interface TransferAccountSelectorProps { memo: string; setMemo: (value: string) => void; spkMarkets: Market[]; + getRecurrentTransferOfUser: (username: string) => string; } const TransferAccountSelector = ({ @@ -50,6 +51,7 @@ const TransferAccountSelector = ({ memo, setMemo, spkMarkets, + getRecurrentTransferOfUser, }: TransferAccountSelectorProps) => { const intl = useIntl(); const destinationRef = useRef(''); @@ -92,6 +94,20 @@ const TransferAccountSelector = ({ return; } const isValid = res.includes(username); + + + if (isValid) { + console.log('===================================='); + console.log('debounce getRecurrentTransferOfUser'); + console.log('===================================='); + + const recurrentTransferOfUser = getRecurrentTransferOfUser(username); + + console.log('===================================='); + console.log('recurrentTransferOfUser', recurrentTransferOfUser); + console.log('===================================='); + } + setIsUsernameValid(isValid); }); }, 300), diff --git a/src/components/transferAmountInputSection/transferAmountInputSection.tsx b/src/components/transferAmountInputSection/transferAmountInputSection.tsx index fa5b6f6c1..a3477160e 100644 --- a/src/components/transferAmountInputSection/transferAmountInputSection.tsx +++ b/src/components/transferAmountInputSection/transferAmountInputSection.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { useIntl } from 'react-intl'; import { Text, TouchableOpacity, View } from 'react-native'; import TextInput from '../textInput'; @@ -7,6 +7,7 @@ import { TransferFormItem } from '../transferFormItem'; // Styles import styles from './transferAmountInputSectionStyles'; import TransferTypes from '../../constants/transferTypes'; +import DropdownButton from '../dropdownButton'; export interface TransferAmountInputSectionProps { balance: number; @@ -20,6 +21,8 @@ export interface TransferAmountInputSectionProps { setAmount: (value: string) => void; recurrence: string; setRecurrence: (value: string) => void; + executions: string; + setExecutions: (value: string) => void; hsTransfer: boolean; transferType: string; selectedAccount: any; @@ -28,6 +31,24 @@ export interface TransferAmountInputSectionProps { disableMinimum?: boolean; } +export const RECURRENCE_TYPES = [ + { + key: 'daily', + hours: 24, + intlId: 'leaderboard.daily', + }, + { + key: 'weekly', + hours: 168, + intlId: 'leaderboard.weekly', + }, + { + key: 'monthly', + hours: 731, + intlId: 'leaderboard.monthly', + }, +]; + const TransferAmountInputSection = ({ balance, getAccountsWithUsername, @@ -40,35 +61,55 @@ const TransferAmountInputSection = ({ setAmount, recurrence, setRecurrence, + executions, + setExecutions, transferType, fundType, disableMinimum, + recurrentTransfers }) => { const intl = useIntl(); + console.log('===================================='); + console.log('destination is: ', destination); + console.log('===================================='); + + + useEffect(() => { + //todo: find recurrent transfer of selected user + }, [recurrentTransfers]) + const _handleOnChange = (state, val) => { - let _amount = val.toString(); - if (_amount.includes(',')) { - _amount = val.replace(',', '.'); + let newValue = val.toString(); + + console.log('===================================='); + console.log('state is: ', state); + console.log('===================================='); + + if (newValue.includes(',')) { + newValue = val.replace(',', '.'); } if (state === 'amount') { - if (parseFloat(Number(_amount)) <= parseFloat(balance)) { - setAmount(_amount); + if (parseFloat(Number(newValue)) <= parseFloat(balance)) { + setAmount(newValue); } - } - if (state === 'destination') { + } else if (state === 'destination') { getAccountsWithUsername(val).then((res) => { + + console.log('===================================='); + console.log('res is'); + console.log('===================================='); + console.log(res); + const isValid = res.includes(val); setIsUsernameValid(isValid); }); - setDestination(_amount); - } - if (state === 'memo') { - setMemo(_amount); - } - if (state === 'recurrence') { - setRecurrence(val); + setDestination(newValue); + } else if (state === 'memo') { + setMemo(newValue); + } else if (state === 'executions') { + setExecutions(val); } }; @@ -85,6 +126,8 @@ const TransferAmountInputSection = ({ ? memo : state === 'recurrence' ? recurrence + : state === 'executions' + ? executions : '' } placeholder={placeholder} @@ -96,6 +139,16 @@ const TransferAmountInputSection = ({ /> ); + const [recurrenceIndex, setRecurrenceIndex] = useState( + RECURRENCE_TYPES.findIndex((r) => r.hours === recurrence) || 0, + ); + + const _handleRecurrenceChange = (index: number) => { + setRecurrenceIndex(index); + + setRecurrence(RECURRENCE_TYPES[index].hours); + }; + const _renderDescription = (text) => {text}; const _renderCenterDescription = (text) => {text}; @@ -134,17 +187,35 @@ const TransferAmountInputSection = ({ )} /> {transferType === TransferTypes.RECURRENT_TRANSFER && ( - - _renderInput( - intl.formatMessage({ id: 'transfer.recurrence_placeholder' }), - 'recurrence', - 'numeric', - false, - ) - } - /> + <> + ( + intl.formatMessage({ id: k.intlId }))} + defaultText={intl.formatMessage({ id: 'transfer.recurrence_placeholder' })} + selectedOptionIndex={recurrenceIndex} + onSelect={index => _handleRecurrenceChange(index)} + /> + )} + /> + + _renderInput( + intl.formatMessage({ id: 'transfer.executions_placeholder' }), + 'executions', + 'numeric', + false, + ) + } + /> + )} {(transferType === TransferTypes.POINTS || transferType === TransferTypes.TRANSFER_TOKEN || diff --git a/src/components/transferAmountInputSection/transferAmountInputSectionStyles.ts b/src/components/transferAmountInputSection/transferAmountInputSectionStyles.ts index d3df54d9b..88958ae8e 100644 --- a/src/components/transferAmountInputSection/transferAmountInputSectionStyles.ts +++ b/src/components/transferAmountInputSection/transferAmountInputSectionStyles.ts @@ -82,4 +82,28 @@ export default EStyleSheet.create({ fontWeight: '600', textAlign: 'left', }, + dropdownText: { + fontSize: 14, + paddingLeft: 12, + paddingHorizontal: 14, + // color: '$primaryDarkGray', + color: '$primaryBlack', + }, + dropdownStyle: { + marginTop: 15, + minWidth: 192, + width: 192, + maxHeight: 300, + }, + dropdownButtonStyle: { + borderColor: '$borderColor', + borderWidth: 1, + height: 44, + width: '100%', + borderRadius: 8, + }, + dropdown: { + flexGrow: 1, + width: 150, + }, }); diff --git a/src/config/locales/en-US.json b/src/config/locales/en-US.json index 9626499b1..85f7c8df6 100644 --- a/src/config/locales/en-US.json +++ b/src/config/locales/en-US.json @@ -771,6 +771,7 @@ "to": "To", "amount_information": "Drag the slider to adjust the amount", "amount": "Amount", + "executions": "Executions", "recurrence": "Recurrence", "memo": "Memo", "recurrent_transfer": "Recurrent Transfer", @@ -780,7 +781,8 @@ "convert_desc": "Convert takes 3.5 days and NOT recommended IF HBD price is higher than $1", "to_placeholder": "Username", "memo_placeholder": "Enter your notes here", - "recurrence_placeholder": "Recurrence for Months", + "recurrence_placeholder": "Choose Recurrence", + "executions_placeholder": "Number of Executions", "transfer_token": "Transfer", "purchase_estm": "Purchase Points", "convert": "Convert HBD to HIVE", diff --git a/src/containers/transferContainer.js b/src/containers/transferContainer.js index b5bd42f40..22bff4964 100644 --- a/src/containers/transferContainer.js +++ b/src/containers/transferContainer.js @@ -17,6 +17,7 @@ import { delegateVestingShares, setWithdrawVestingRoute, recurrentTransferToken, + getRecurrentTransfers, } from '../providers/hive/dhive'; import { toastNotification } from '../redux/actions/uiAction'; import { getUserDataWithUsername } from '../realm/realm'; @@ -44,6 +45,7 @@ import { fetchSpkMarkets, } from '../providers/hive-spk/hiveSpk'; import { SpkLockMode, SpkPowerMode } from '../providers/hive-spk/hiveSpk.types'; +import { log } from '../../reactotron-config'; /* * Props Name Description Value @@ -65,6 +67,8 @@ class TransferContainer extends Component { initialAmount: props.route.params?.initialAmount, initialMemo: props.route.params?.initialMemo, initialRecurrence: props.route.params?.initialRecurrence, + initialExecutions: props.route.params?.initialExecutions, + recurrentTransfers: [], }; } @@ -76,6 +80,8 @@ class TransferContainer extends Component { this.fetchBalance(name); + this._fetchRecurrentTransfers(name); + if (this.state.transferType === TransferTypes.DELEGATE_SPK) { this._fetchSpkMarkets(); } @@ -102,6 +108,8 @@ class TransferContainer extends Component { getAccount(username).then(async (account) => { let balance; + log('account is', account); + if (transferType.endsWith('_engine')) { const tokenBalances = await fetchTokenBalances(username); @@ -183,6 +191,21 @@ class TransferContainer extends Component { return validUsers; }; + _fetchRecurrentTransfers = async (username) => { + const recTransfers = await getRecurrentTransfers(username); + + log('===================================='); + log('recurrent transfers'); + log('===================================='); + log(recTransfers); + + this.setState({ + recurrentTransfers: recTransfers, + }); + + return recTransfers; + }; + _delayedRefreshCoinsData = () => { const { dispatch } = this.props; setTimeout(() => { @@ -190,7 +213,14 @@ class TransferContainer extends Component { }, 3000); }; - _transferToAccount = async (from, destination, amount, memo, recurrence = null) => { + _transferToAccount = async ( + from, + destination, + amount, + memo, + recurrence = null, + executions = 0, + ) => { const { pinCode, navigation, dispatch, intl, route } = this.props; let { currentAccount } = this.props; const { selectedAccount } = this.state; @@ -207,8 +237,9 @@ class TransferContainer extends Component { fundType, }; - if (recurrence) { + if (recurrence && executions) { data.recurrence = +recurrence; + data.executions = +executions; } if (countDecimals(Number(data.amount)) < 3) { @@ -307,6 +338,10 @@ class TransferContainer extends Component { navigation.goBack(); }) .catch((err) => { + log('===================================='); + log('error got'); + log('===================================='); + log(err); navigation.goBack(); bugsnagInstance.notify(err); dispatch(toastNotification(intl.formatMessage({ id: 'alert.key_warning' }))); @@ -354,6 +389,8 @@ class TransferContainer extends Component { initialAmount, initialMemo, initialRecurrence, + initialExecutions, + recurrentTransfers, } = this.state; const transferType = route.params?.transferType ?? ''; @@ -382,6 +419,9 @@ class TransferContainer extends Component { initialAmount, initialMemo, initialRecurrence, + initialExecutions, + fetchRecurrentTransfers: this._fetchRecurrentTransfers, + recurrentTransfers, }) ); } diff --git a/src/providers/hive/dhive.js b/src/providers/hive/dhive.js index 6b1bbe7d4..4af3e0a5e 100644 --- a/src/providers/hive/dhive.js +++ b/src/providers/hive/dhive.js @@ -42,7 +42,6 @@ import { SERVER_LIST } from '../../constants/options/api'; import { b64uEnc } from '../../utils/b64'; import bugsnagInstance from '../../config/bugsnag'; import bugsnapInstance from '../../config/bugsnag'; -import { makeJsonMetadataReply } from '../../utils/editor'; import TransferTypes from '../../constants/transferTypes'; const hiveuri = require('hive-uri'); @@ -1005,7 +1004,10 @@ export const recurrentTransferToken = (currentAccount, pin, data) => { amount: get(data, 'amount'), memo: get(data, 'memo'), recurrence: get(data, 'recurrence'), + executions: get(data, 'executions'), + extensions: [], }; + const opArray = [[TransferTypes.RECURRENT_TRANSFER, args]]; return new Promise((resolve, reject) => { @@ -1495,6 +1497,19 @@ export const getTrendingTags = async (tag, number = 20) => { } }; +export const getRecurrentTransfers = async (username) => { + try { + const rawData = await client.call('condenser_api', 'find_recurrent_transfers', [username]); + if (!rawData || !rawData.length) { + return []; + } + return rawData; + } catch (err) { + console.warn('Failed to get recurrent transfers', err); + return []; + } +}; + export const postContent = ( account, pin, diff --git a/src/redux/reducers/walletReducer.ts b/src/redux/reducers/walletReducer.ts index 7cbe28214..72789fe17 100644 --- a/src/redux/reducers/walletReducer.ts +++ b/src/redux/reducers/walletReducer.ts @@ -60,6 +60,7 @@ export interface CoinActivity { memo: string; cancelable: boolean; recurrence: string; + executions: string; } export interface QuoteItem { diff --git a/src/screens/assetDetails/screen/assetDetailsScreen.tsx b/src/screens/assetDetails/screen/assetDetailsScreen.tsx index 63fa66b6a..dc6e3c5a6 100644 --- a/src/screens/assetDetails/screen/assetDetailsScreen.tsx +++ b/src/screens/assetDetails/screen/assetDetailsScreen.tsx @@ -15,6 +15,7 @@ import { DelegationsModal, MODES } from '../children/delegationsModal'; import TransferTypes from '../../../constants/transferTypes'; import { walletQueries } from '../../../providers/queries'; import parseToken from '../../../utils/parseToken'; +import { log } from '../../../../reactotron-config'; export interface AssetDetailsScreenParams { coinId: string; @@ -157,6 +158,7 @@ const AssetDetailsScreen = ({ navigation, route }: AssetDetailsScreenProps) => { initialAmount: `${parseToken(baseActivity.value)}`, initialMemo: baseActivity.memo, initialRecurrence: `${parseToken(baseActivity.recurrence)}`, + initialExecutions: `${parseToken(baseActivity.executions)}`, }; } diff --git a/src/screens/transfer/index.js b/src/screens/transfer/index.js index bc7c3c16d..366d39624 100644 --- a/src/screens/transfer/index.js +++ b/src/screens/transfer/index.js @@ -31,6 +31,9 @@ const Transfer = ({ navigation, route }) => ( initialAmount, initialMemo, initialRecurrence, + initialExecutions, + recurrentTransfers, + fetchRecurrentTransfers, }) => { switch (transferType) { case 'delegate': @@ -100,6 +103,9 @@ const Transfer = ({ navigation, route }) => ( initialAmount={initialAmount || ''} initialMemo={initialMemo || ''} initialRecurrence={initialRecurrence || ''} + initialExecutions={initialExecutions || ''} + recurrentTransfers={recurrentTransfers || []} + fetchRecurrentTransfers={fetchRecurrentTransfers} /> ); } diff --git a/src/screens/transfer/screen/transferScreen.js b/src/screens/transfer/screen/transferScreen.js index 006e3285e..bc66670d3 100644 --- a/src/screens/transfer/screen/transferScreen.js +++ b/src/screens/transfer/screen/transferScreen.js @@ -1,4 +1,4 @@ -import React, { useState, useMemo } from 'react'; +import React, { useState, useMemo, useEffect } from 'react'; import { Alert, Text, View } from 'react-native'; import { WebView } from 'react-native-webview'; import { injectIntl } from 'react-intl'; @@ -26,6 +26,7 @@ import { getSpkTransactionId, SPK_NODE_ECENCY, } from '../../../providers/hive-spk/hiveSpk'; +import { RECURRENCE_TYPES } from '../../../components/transferAmountInputSection/transferAmountInputSection'; const TransferView = ({ currentAccountName, @@ -45,6 +46,9 @@ const TransferView = ({ initialAmount, initialMemo, initialRecurrence, + initialExecutions, + fetchRecurrentTransfers, + recurrentTransfers, }) => { const dispatch = useAppDispatch(); @@ -75,7 +79,8 @@ const TransferView = ({ const [memo, setMemo] = useState( transferType === 'purchase_estm' ? 'estm-purchase' : initialMemo, ); - const [recurrence, setRecurrence] = useState(`${initialRecurrence}`); + const [recurrence, setRecurrence] = useState(RECURRENCE_TYPES[2].hours); + const [executions, setExecutions] = useState(''); const [isUsernameValid, setIsUsernameValid] = useState( !!( @@ -98,6 +103,8 @@ const TransferView = ({ const [hsTransfer, setHsTransfer] = useState(false); const [isTransfering, setIsTransfering] = useState(false); + const isRecurrentTransfer = transferType === TransferTypes.RECURRENT_TRANSFER; + const isEngineToken = useMemo(() => transferType.endsWith('_engine'), [transferType]); const isSpkToken = useMemo(() => transferType.endsWith('_spk'), [transferType]); @@ -112,7 +119,8 @@ const TransferView = ({ destination, amount, memo, - transferType === TransferTypes.RECURRENT_TRANSFER ? recurrence : null, + isRecurrentTransfer ? recurrence : null, + isRecurrentTransfer ? executions : null, ); } }, @@ -144,7 +152,7 @@ const TransferView = ({ if (transferType === TransferTypes.RECURRENT_TRANSFER) { path = `sign/recurrent_transfer?from=${currentAccountName}&to=${destination}&amount=${encodeURIComponent( `${amount} ${fundType}`, - )}&memo=${encodeURIComponent(memo)}&recurrence=${recurrence}`; + )}&memo=${encodeURIComponent(memo)}&recurrence=${recurrence}&executions=${executions}`; } else if (transferType === TransferTypes.TRANSFER_TO_SAVINGS) { path = `sign/transfer_to_savings?from=${currentAccountName}&to=${destination}&amount=${encodeURIComponent( `${amount} ${fundType}`, @@ -241,6 +249,39 @@ const TransferView = ({ const nextBtnDisabled = !((isEngineToken ? amount > 0 : amount >= 0.001) && isUsernameValid); + useEffect(() => { + if (isRecurrentTransfer) { + fetchRecurrentTransfers(currentAccountName); + } + }, [isRecurrentTransfer]); + + + useEffect(() => { + console.log('===================================='); + console.log('recurrentTransfers in transferScreen'); + console.log('===================================='); + console.log(recurrentTransfers); + }, [recurrentTransfers]) + + const _findRecurrentTransferOfUser = (userToFind) => { + if (!isRecurrentTransfer) { + return false; + } + + console.log('===================================='); + console.log('try to find recurrent transfer of user: ', userToFind); + console.log('===================================='); + + //todo:gu fix issue with recurrentTransfers here + const recurrentTransferOfUser = recurrentTransfers.find((rt) => rt.to === userToFind); + + console.log('===================================='); + console.log(recurrentTransferOfUser || recurrentTransfers); + console.log('===================================='); + + return recurrentTransferOfUser; + }; + return ( Date: Wed, 25 Oct 2023 10:21:59 +0500 Subject: [PATCH 4/8] fix: GU-2746 add support for reurrent transfers, fetch existing recurrent transfer in progress, merge with dev --- ios/Podfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index d56f1933b..955e8a17c 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1110,6 +1110,6 @@ SPEC CHECKSUMS: Yoga: 92d086bb705a41cc588599b51db726ba7b1d341c YogaKit: f782866e155069a2cca2517aafea43200b01fd5a -PODFILE CHECKSUM: 6b212d63236c21489948e9b73b59fcff2feeeb08 +PODFILE CHECKSUM: 04593ac2ffe011afd4b99c759aa500a38a73c8bd COCOAPODS: 1.13.0 From 272a1d33c358af5e1002dccee58fcbfeff35ff04 Mon Sep 17 00:00:00 2001 From: gamingumar Date: Fri, 27 Oct 2023 01:27:56 +0500 Subject: [PATCH 5/8] fix: GU-2746 prefill recurrent transfer in form --- ios/Podfile.lock | 2 +- .../transferAccountSelector.tsx | 6 +- .../transferAmountInputSection.tsx | 38 +++++++---- src/screens/transfer/screen/transferScreen.js | 66 +++++++++++++------ 4 files changed, 73 insertions(+), 39 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 955e8a17c..d56f1933b 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1110,6 +1110,6 @@ SPEC CHECKSUMS: Yoga: 92d086bb705a41cc588599b51db726ba7b1d341c YogaKit: f782866e155069a2cca2517aafea43200b01fd5a -PODFILE CHECKSUM: 04593ac2ffe011afd4b99c759aa500a38a73c8bd +PODFILE CHECKSUM: 6b212d63236c21489948e9b73b59fcff2feeeb08 COCOAPODS: 1.13.0 diff --git a/src/components/transferAccountSelector/transferAccountSelector.tsx b/src/components/transferAccountSelector/transferAccountSelector.tsx index d48469574..d67da1fbf 100644 --- a/src/components/transferAccountSelector/transferAccountSelector.tsx +++ b/src/components/transferAccountSelector/transferAccountSelector.tsx @@ -97,10 +97,6 @@ const TransferAccountSelector = ({ if (isValid) { - console.log('===================================='); - console.log('debounce getRecurrentTransferOfUser'); - console.log('===================================='); - const recurrentTransferOfUser = getRecurrentTransferOfUser(username); console.log('===================================='); @@ -111,7 +107,7 @@ const TransferAccountSelector = ({ setIsUsernameValid(isValid); }); }, 300), - [], + [getRecurrentTransferOfUser], ); const _handleOnChange = (state: string, val: string) => { diff --git a/src/components/transferAmountInputSection/transferAmountInputSection.tsx b/src/components/transferAmountInputSection/transferAmountInputSection.tsx index a3477160e..1e7c445bc 100644 --- a/src/components/transferAmountInputSection/transferAmountInputSection.tsx +++ b/src/components/transferAmountInputSection/transferAmountInputSection.tsx @@ -66,7 +66,6 @@ const TransferAmountInputSection = ({ transferType, fundType, disableMinimum, - recurrentTransfers }) => { const intl = useIntl(); @@ -74,11 +73,6 @@ const TransferAmountInputSection = ({ console.log('destination is: ', destination); console.log('===================================='); - - useEffect(() => { - //todo: find recurrent transfer of selected user - }, [recurrentTransfers]) - const _handleOnChange = (state, val) => { let newValue = val.toString(); @@ -95,7 +89,6 @@ const TransferAmountInputSection = ({ } } else if (state === 'destination') { getAccountsWithUsername(val).then((res) => { - console.log('===================================='); console.log('res is'); console.log('===================================='); @@ -116,7 +109,7 @@ const TransferAmountInputSection = ({ const _renderInput = (placeholder, state, keyboardType, isTextArea) => ( _handleOnChange(state, amount)} + onChangeText={(newVal) => _handleOnChange(state, newVal)} value={ state === 'destination' ? destination @@ -124,9 +117,9 @@ const TransferAmountInputSection = ({ ? amount : state === 'memo' ? memo - : state === 'recurrence' - ? recurrence - : state === 'executions' + : // : state === 'recurrence' + // ? recurrence + state === 'executions' ? executions : '' } @@ -140,9 +133,21 @@ const TransferAmountInputSection = ({ ); const [recurrenceIndex, setRecurrenceIndex] = useState( - RECURRENCE_TYPES.findIndex((r) => r.hours === recurrence) || 0, + RECURRENCE_TYPES.findIndex((r) => r.hours === recurrence), ); + useEffect(() => { + console.log('===================================='); + console.log( + 'updating recurrence index', + recurrence !== '' && recurrenceIndex > -1, + recurrence, + recurrenceIndex, + ); + console.log('===================================='); + setRecurrenceIndex(RECURRENCE_TYPES.findIndex((r) => r.hours === recurrence)); + }, [recurrence]);//todo: fix why this is not updating... + const _handleRecurrenceChange = (index: number) => { setRecurrenceIndex(index); @@ -198,9 +203,14 @@ const TransferAmountInputSection = ({ dropdownStyle={styles.dropdownStyle} textStyle={styles.dropdownText} options={RECURRENCE_TYPES.map((k) => intl.formatMessage({ id: k.intlId }))} - defaultText={intl.formatMessage({ id: 'transfer.recurrence_placeholder' })} + defaultText={intl.formatMessage({ + id: + recurrence !== '' && recurrenceIndex > -1 + ? RECURRENCE_TYPES[recurrenceIndex].intlId + : 'transfer.recurrence_placeholder', + })} selectedOptionIndex={recurrenceIndex} - onSelect={index => _handleRecurrenceChange(index)} + onSelect={(index) => _handleRecurrenceChange(index)} /> )} /> diff --git a/src/screens/transfer/screen/transferScreen.js b/src/screens/transfer/screen/transferScreen.js index bc66670d3..9cb8b4bd9 100644 --- a/src/screens/transfer/screen/transferScreen.js +++ b/src/screens/transfer/screen/transferScreen.js @@ -1,4 +1,4 @@ -import React, { useState, useMemo, useEffect } from 'react'; +import React, { useState, useMemo, useEffect, useCallback } from 'react'; import { Alert, Text, View } from 'react-native'; import { WebView } from 'react-native-webview'; import { injectIntl } from 'react-intl'; @@ -27,6 +27,7 @@ import { SPK_NODE_ECENCY, } from '../../../providers/hive-spk/hiveSpk'; import { RECURRENCE_TYPES } from '../../../components/transferAmountInputSection/transferAmountInputSection'; +import parseToken from '../../../utils/parseToken'; const TransferView = ({ currentAccountName, @@ -79,7 +80,7 @@ const TransferView = ({ const [memo, setMemo] = useState( transferType === 'purchase_estm' ? 'estm-purchase' : initialMemo, ); - const [recurrence, setRecurrence] = useState(RECURRENCE_TYPES[2].hours); + const [recurrence, setRecurrence] = useState(''); const [executions, setExecutions] = useState(''); const [isUsernameValid, setIsUsernameValid] = useState( @@ -255,32 +256,60 @@ const TransferView = ({ } }, [isRecurrentTransfer]); - useEffect(() => { console.log('===================================='); console.log('recurrentTransfers in transferScreen'); console.log('===================================='); console.log(recurrentTransfers); - }, [recurrentTransfers]) + }, [recurrentTransfers]); - const _findRecurrentTransferOfUser = (userToFind) => { - if (!isRecurrentTransfer) { - return false; - } + const _findRecurrentTransferOfUser = useCallback( + (userToFind) => { + console.log('===================================='); + console.log('try to find recurrent transfer of user: ', userToFind); + console.log('===================================='); + console.log('recurrentTransfers', recurrentTransfers); - console.log('===================================='); - console.log('try to find recurrent transfer of user: ', userToFind); - console.log('===================================='); + if (!isRecurrentTransfer) { + return false; + } - //todo:gu fix issue with recurrentTransfers here - const recurrentTransferOfUser = recurrentTransfers.find((rt) => rt.to === userToFind); + const existingRecurrentTransfer = recurrentTransfers.find((rt) => rt.to === userToFind); - console.log('===================================='); - console.log(recurrentTransferOfUser || recurrentTransfers); - console.log('===================================='); + console.log('===================================='); + console.log(existingRecurrentTransfer || recurrentTransfers); + console.log('===================================='); - return recurrentTransferOfUser; - }; + let newMemo, + newAmount, + newRecurrence, + newExecutions = ''; + + if (existingRecurrentTransfer) { + newMemo = existingRecurrentTransfer.memo; + newAmount = parseToken(existingRecurrentTransfer.amount).toString(); + newRecurrence = existingRecurrentTransfer.recurrence; + newExecutions = `${existingRecurrentTransfer.remaining_executions}`; + + console.log('===================================='); + console.log('setting new data', { + newAmount, + newMemo, + newRecurrence, + newExecutions, + }); + console.log('===================================='); + } + + setMemo(newMemo); + setAmount(newAmount); + setRecurrence(newRecurrence); + setExecutions(newExecutions); + + return existingRecurrentTransfer; + }, + [recurrentTransfers], + ); return ( @@ -333,7 +362,6 @@ const TransferView = ({ setRecurrence={setRecurrence} executions={executions} setExecutions={setExecutions} - recurrentTransfer={recurrentTransfers} /> Date: Fri, 27 Oct 2023 19:04:30 +0500 Subject: [PATCH 6/8] feat: GU-2746 prefill recurrent transfer --- .../transferAccountSelector.tsx | 7 +-- .../transferAmountInputSection.tsx | 43 ++++++------------- src/containers/transferContainer.js | 9 ---- src/screens/transfer/screen/transferScreen.js | 40 ++++------------- 4 files changed, 22 insertions(+), 77 deletions(-) diff --git a/src/components/transferAccountSelector/transferAccountSelector.tsx b/src/components/transferAccountSelector/transferAccountSelector.tsx index d67da1fbf..6d903184f 100644 --- a/src/components/transferAccountSelector/transferAccountSelector.tsx +++ b/src/components/transferAccountSelector/transferAccountSelector.tsx @@ -84,7 +84,6 @@ const TransferAccountSelector = ({ setDestination(username); }; - //todo: fetch existing recurrent transfer const _debouncedValidateUsername = useCallback( debounce((username: string) => { getAccountsWithUsername(username).then((res) => { @@ -97,11 +96,7 @@ const TransferAccountSelector = ({ if (isValid) { - const recurrentTransferOfUser = getRecurrentTransferOfUser(username); - - console.log('===================================='); - console.log('recurrentTransferOfUser', recurrentTransferOfUser); - console.log('===================================='); + getRecurrentTransferOfUser(username); } setIsUsernameValid(isValid); diff --git a/src/components/transferAmountInputSection/transferAmountInputSection.tsx b/src/components/transferAmountInputSection/transferAmountInputSection.tsx index 1e7c445bc..bbe725af3 100644 --- a/src/components/transferAmountInputSection/transferAmountInputSection.tsx +++ b/src/components/transferAmountInputSection/transferAmountInputSection.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { useIntl } from 'react-intl'; import { Text, TouchableOpacity, View } from 'react-native'; import TextInput from '../textInput'; @@ -69,17 +69,11 @@ const TransferAmountInputSection = ({ }) => { const intl = useIntl(); - console.log('===================================='); - console.log('destination is: ', destination); - console.log('===================================='); + const dpRef = useRef(); const _handleOnChange = (state, val) => { let newValue = val.toString(); - console.log('===================================='); - console.log('state is: ', state); - console.log('===================================='); - if (newValue.includes(',')) { newValue = val.replace(',', '.'); } @@ -89,9 +83,6 @@ const TransferAmountInputSection = ({ } } else if (state === 'destination') { getAccountsWithUsername(val).then((res) => { - console.log('===================================='); - console.log('res is'); - console.log('===================================='); console.log(res); const isValid = res.includes(val); @@ -117,9 +108,7 @@ const TransferAmountInputSection = ({ ? amount : state === 'memo' ? memo - : // : state === 'recurrence' - // ? recurrence - state === 'executions' + : state === 'executions' ? executions : '' } @@ -137,16 +126,14 @@ const TransferAmountInputSection = ({ ); useEffect(() => { - console.log('===================================='); - console.log( - 'updating recurrence index', - recurrence !== '' && recurrenceIndex > -1, - recurrence, - recurrenceIndex, - ); - console.log('===================================='); - setRecurrenceIndex(RECURRENCE_TYPES.findIndex((r) => r.hours === recurrence)); - }, [recurrence]);//todo: fix why this is not updating... + const newSelectedIndex = RECURRENCE_TYPES.findIndex((r) => r.hours === +recurrence); + + setRecurrenceIndex(newSelectedIndex); + + if (dpRef?.current) { + dpRef.current.select(newSelectedIndex); + } + }, [recurrence, dpRef]); const _handleRecurrenceChange = (index: number) => { setRecurrenceIndex(index); @@ -203,14 +190,10 @@ const TransferAmountInputSection = ({ dropdownStyle={styles.dropdownStyle} textStyle={styles.dropdownText} options={RECURRENCE_TYPES.map((k) => intl.formatMessage({ id: k.intlId }))} - defaultText={intl.formatMessage({ - id: - recurrence !== '' && recurrenceIndex > -1 - ? RECURRENCE_TYPES[recurrenceIndex].intlId - : 'transfer.recurrence_placeholder', - })} + defaultText={intl.formatMessage({ id: 'transfer.recurrence_placeholder' })} selectedOptionIndex={recurrenceIndex} onSelect={(index) => _handleRecurrenceChange(index)} + dropdownRef={dpRef} /> )} /> diff --git a/src/containers/transferContainer.js b/src/containers/transferContainer.js index 22bff4964..cf4b9b8dc 100644 --- a/src/containers/transferContainer.js +++ b/src/containers/transferContainer.js @@ -194,11 +194,6 @@ class TransferContainer extends Component { _fetchRecurrentTransfers = async (username) => { const recTransfers = await getRecurrentTransfers(username); - log('===================================='); - log('recurrent transfers'); - log('===================================='); - log(recTransfers); - this.setState({ recurrentTransfers: recTransfers, }); @@ -338,10 +333,6 @@ class TransferContainer extends Component { navigation.goBack(); }) .catch((err) => { - log('===================================='); - log('error got'); - log('===================================='); - log(err); navigation.goBack(); bugsnagInstance.notify(err); dispatch(toastNotification(intl.formatMessage({ id: 'alert.key_warning' }))); diff --git a/src/screens/transfer/screen/transferScreen.js b/src/screens/transfer/screen/transferScreen.js index 9cb8b4bd9..c660e4480 100644 --- a/src/screens/transfer/screen/transferScreen.js +++ b/src/screens/transfer/screen/transferScreen.js @@ -256,30 +256,14 @@ const TransferView = ({ } }, [isRecurrentTransfer]); - useEffect(() => { - console.log('===================================='); - console.log('recurrentTransfers in transferScreen'); - console.log('===================================='); - console.log(recurrentTransfers); - }, [recurrentTransfers]); - const _findRecurrentTransferOfUser = useCallback( (userToFind) => { - console.log('===================================='); - console.log('try to find recurrent transfer of user: ', userToFind); - console.log('===================================='); - console.log('recurrentTransfers', recurrentTransfers); - if (!isRecurrentTransfer) { return false; } const existingRecurrentTransfer = recurrentTransfers.find((rt) => rt.to === userToFind); - console.log('===================================='); - console.log(existingRecurrentTransfer || recurrentTransfers); - console.log('===================================='); - let newMemo, newAmount, newRecurrence, @@ -288,25 +272,17 @@ const TransferView = ({ if (existingRecurrentTransfer) { newMemo = existingRecurrentTransfer.memo; newAmount = parseToken(existingRecurrentTransfer.amount).toString(); - newRecurrence = existingRecurrentTransfer.recurrence; + newRecurrence = existingRecurrentTransfer.recurrence.toString(); newExecutions = `${existingRecurrentTransfer.remaining_executions}`; - console.log('===================================='); - console.log('setting new data', { - newAmount, - newMemo, - newRecurrence, - newExecutions, - }); - console.log('===================================='); + setMemo(newMemo); + setAmount(newAmount); + setRecurrence(newRecurrence); + setExecutions(newExecutions); + + return existingRecurrentTransfer; } - - setMemo(newMemo); - setAmount(newAmount); - setRecurrence(newRecurrence); - setExecutions(newExecutions); - - return existingRecurrentTransfer; + return null; }, [recurrentTransfers], ); From 6e4cb27c01ad781d4b7d8ab53f33eaec57c0f685 Mon Sep 17 00:00:00 2001 From: gamingumar Date: Fri, 27 Oct 2023 22:27:37 +0500 Subject: [PATCH 7/8] feat: GU-2746 delete recurrent transfer option and cleanup --- .../services/tabbedPostsHelpers.ts | 2 +- .../transferAmountInputSection.tsx | 38 +++++++++++++--- .../transferAmountInputSectionStyles.ts | 4 ++ src/config/api.js | 4 +- src/config/ecencyApi.ts | 4 +- src/config/imageApi.js | 4 +- src/config/locales/en-US.json | 1 + src/config/serverListApi.js | 4 +- src/containers/transferContainer.js | 9 ---- src/providers/hive/dhive.js | 4 ++ src/providers/queries/notificationQueries.ts | 2 +- .../queries/postQueries/postQueries.ts | 2 +- .../queries/walletQueries/walletQueries.ts | 2 +- src/redux/reducers/walkthroughReducer.ts | 2 +- .../screen/assetDetailsScreen.tsx | 2 - src/screens/transfer/index.js | 4 -- src/screens/transfer/screen/transferScreen.js | 43 +++++++++++++------ src/utils/time.js | 2 +- 18 files changed, 85 insertions(+), 48 deletions(-) diff --git a/src/components/tabbedPosts/services/tabbedPostsHelpers.ts b/src/components/tabbedPosts/services/tabbedPostsHelpers.ts index 3ca4c5b71..a27b9517d 100644 --- a/src/components/tabbedPosts/services/tabbedPostsHelpers.ts +++ b/src/components/tabbedPosts/services/tabbedPostsHelpers.ts @@ -19,7 +19,7 @@ export const calculateTimeLeftForPostCheck = (firstPost: any) => { // filter posts that are not present in top 5 posts currently in list. export const filterLatestPosts = (fetchedPosts: any[], cachedPosts: any[]) => { - console.log('Comparing: ', fetchedPosts, cachedPosts); + // console.log('Comparing: ', fetchedPosts, cachedPosts); const latestPosts = []; fetchedPosts.forEach((post) => { diff --git a/src/components/transferAmountInputSection/transferAmountInputSection.tsx b/src/components/transferAmountInputSection/transferAmountInputSection.tsx index bbe725af3..ccec97c7a 100644 --- a/src/components/transferAmountInputSection/transferAmountInputSection.tsx +++ b/src/components/transferAmountInputSection/transferAmountInputSection.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import { useIntl } from 'react-intl'; import { Text, TouchableOpacity, View } from 'react-native'; import TextInput from '../textInput'; @@ -8,6 +8,7 @@ import { TransferFormItem } from '../transferFormItem'; import styles from './transferAmountInputSectionStyles'; import TransferTypes from '../../constants/transferTypes'; import DropdownButton from '../dropdownButton'; +import { dateToFormatted } from '../../utils/time'; export interface TransferAmountInputSectionProps { balance: number; @@ -59,13 +60,15 @@ const TransferAmountInputSection = ({ setMemo, amount, setAmount, + fundType, + disableMinimum, + transferType, recurrence, setRecurrence, executions, setExecutions, - transferType, - fundType, - disableMinimum, + startDate, + onNext, }) => { const intl = useIntl(); @@ -130,19 +133,29 @@ const TransferAmountInputSection = ({ setRecurrenceIndex(newSelectedIndex); + if (newSelectedIndex > -1) { + setRecurrence(RECURRENCE_TYPES[newSelectedIndex].hours); + } + if (dpRef?.current) { dpRef.current.select(newSelectedIndex); } }, [recurrence, dpRef]); - const _handleRecurrenceChange = (index: number) => { + const _handleRecurrenceChange = useCallback((index: number) => { setRecurrenceIndex(index); setRecurrence(RECURRENCE_TYPES[index].hours); - }; + }, []); + + const _onDelete = () => { + onNext(true); + } const _renderDescription = (text) => {text}; - const _renderCenterDescription = (text) => {text}; + const _renderCenterDescription = (text, extraStyles = {}) => ( + {text} + ); const amountLimitText = disableMinimum ? '' @@ -159,6 +172,16 @@ const TransferAmountInputSection = ({ { suffix: amountLimitText }, )} + + {startDate && startDate !== '' && ( + + + {intl.formatMessage({ id: 'transfer.delete_recurrent_transfer' }) + + dateToFormatted(startDate, 'LL')} + + + )} + @@ -230,6 +253,7 @@ const TransferAmountInputSection = ({ containerStyle={{ height: 80 }} /> )} + {(transferType === TransferTypes.POINTS || transferType === TransferTypes.TRANSFER_TOKEN) && ( { - console.log('Starting api Request', request); + // console.log('Starting api Request', request); return request; }); api.interceptors.response.use((response) => { - console.log('Response:', response); + // console.log('Response:', response); return response; }); diff --git a/src/config/ecencyApi.ts b/src/config/ecencyApi.ts index 5dc512fad..e4fa86d8b 100644 --- a/src/config/ecencyApi.ts +++ b/src/config/ecencyApi.ts @@ -18,7 +18,7 @@ const ecencyApi = axios.create({ }); ecencyApi.interceptors.request.use((request) => { - console.log('Starting ecency Request', request); + // console.log(`Starting ecency Request`, request); // skip code addition is register and token refresh endpoint is triggered if ( @@ -68,7 +68,7 @@ ecencyApi.interceptors.request.use((request) => { }); ecencyApi.interceptors.response.use((response) => { - console.log('Response:', response); + // console.log('Response:', response); return response; }); diff --git a/src/config/imageApi.js b/src/config/imageApi.js index bdbfadf9b..dcb9b1f0e 100644 --- a/src/config/imageApi.js +++ b/src/config/imageApi.js @@ -12,12 +12,12 @@ function upload(fd, username, signature, uploadProgress) { }); image.interceptors.request.use((request) => { - console.log('Starting image Request', request); + // console.log('Starting image Request', request); return request; }); image.interceptors.response.use((response) => { - console.log('Response:', response); + // console.log('Response:', response); return response; }); diff --git a/src/config/locales/en-US.json b/src/config/locales/en-US.json index 85f7c8df6..47f33005c 100644 --- a/src/config/locales/en-US.json +++ b/src/config/locales/en-US.json @@ -775,6 +775,7 @@ "recurrence": "Recurrence", "memo": "Memo", "recurrent_transfer": "Recurrent Transfer", + "delete_recurrent_transfer": "Delete Recurrent Transfer ", "information": "Continue transaction?", "amount_desc": "Balance", "memo_desc": "This memo is public", diff --git a/src/config/serverListApi.js b/src/config/serverListApi.js index 11d1a72d1..c3e35ec73 100644 --- a/src/config/serverListApi.js +++ b/src/config/serverListApi.js @@ -10,12 +10,12 @@ const slist = axios.create({ }); slist.interceptors.request.use((request) => { - console.log('Starting server list Request', request); + // console.log('Starting server list Request', request); return request; }); slist.interceptors.response.use((response) => { - console.log('Response:', response); + // console.log('Response:', response); return response; }); diff --git a/src/containers/transferContainer.js b/src/containers/transferContainer.js index cf4b9b8dc..898cd61a4 100644 --- a/src/containers/transferContainer.js +++ b/src/containers/transferContainer.js @@ -45,7 +45,6 @@ import { fetchSpkMarkets, } from '../providers/hive-spk/hiveSpk'; import { SpkLockMode, SpkPowerMode } from '../providers/hive-spk/hiveSpk.types'; -import { log } from '../../reactotron-config'; /* * Props Name Description Value @@ -66,8 +65,6 @@ class TransferContainer extends Component { spkMarkets: [], initialAmount: props.route.params?.initialAmount, initialMemo: props.route.params?.initialMemo, - initialRecurrence: props.route.params?.initialRecurrence, - initialExecutions: props.route.params?.initialExecutions, recurrentTransfers: [], }; } @@ -108,8 +105,6 @@ class TransferContainer extends Component { getAccount(username).then(async (account) => { let balance; - log('account is', account); - if (transferType.endsWith('_engine')) { const tokenBalances = await fetchTokenBalances(username); @@ -379,8 +374,6 @@ class TransferContainer extends Component { spkMarkets, initialAmount, initialMemo, - initialRecurrence, - initialExecutions, recurrentTransfers, } = this.state; @@ -409,8 +402,6 @@ class TransferContainer extends Component { setWithdrawVestingRoute: this._setWithdrawVestingRoute, initialAmount, initialMemo, - initialRecurrence, - initialExecutions, fetchRecurrentTransfers: this._fetchRecurrentTransfers, recurrentTransfers, }) diff --git a/src/providers/hive/dhive.js b/src/providers/hive/dhive.js index 4af3e0a5e..a3f0f67a2 100644 --- a/src/providers/hive/dhive.js +++ b/src/providers/hive/dhive.js @@ -1018,6 +1018,10 @@ export const recurrentTransferToken = (currentAccount, pin, data) => { } }) .catch((err) => { + console.log('===================================='); + console.log('error on recurrent transfer token'); + console.log('===================================='); + console.log(err); reject(err); }); }); diff --git a/src/providers/queries/notificationQueries.ts b/src/providers/queries/notificationQueries.ts index 93414a77a..a3badcfc3 100644 --- a/src/providers/queries/notificationQueries.ts +++ b/src/providers/queries/notificationQueries.ts @@ -26,7 +26,7 @@ export const useNotificationsQuery = (filter: NotificationFilters) => { const _fetchNotifications = async (pageParam: string) => { console.log('fetching page since:', pageParam); const response = await getNotifications({ filter, since: pageParam, limit: FETCH_LIMIT }); - console.log('new page fetched', response); + // console.log('new page fetched', response); return response || []; }; diff --git a/src/providers/queries/postQueries/postQueries.ts b/src/providers/queries/postQueries/postQueries.ts index f6eed34a5..b7cffbe5e 100644 --- a/src/providers/queries/postQueries/postQueries.ts +++ b/src/providers/queries/postQueries/postQueries.ts @@ -259,7 +259,7 @@ export const useInjectVotesCache = (_data: any | any[]) => { }; const _cData = isArray(_data) ? _data.map(_itemFunc) : _itemFunc({ ..._data }); - console.log('data received', _cData.length, _cData); + // console.log('data received', _cData.length, _cData); setRetData(_cData); }, [_data]); diff --git a/src/providers/queries/walletQueries/walletQueries.ts b/src/providers/queries/walletQueries/walletQueries.ts index 0ad9a6d19..07ba1c57a 100644 --- a/src/providers/queries/walletQueries/walletQueries.ts +++ b/src/providers/queries/walletQueries/walletQueries.ts @@ -278,7 +278,7 @@ export const useActivitiesQuery = (assetId: string) => { isEngine: assetData.isEngine, }); - console.log('new page fetched', _activites); + // console.log('new page fetched', _activites); return _activites || []; }; diff --git a/src/redux/reducers/walkthroughReducer.ts b/src/redux/reducers/walkthroughReducer.ts index 22bc0c800..39389b5f4 100644 --- a/src/redux/reducers/walkthroughReducer.ts +++ b/src/redux/reducers/walkthroughReducer.ts @@ -12,7 +12,7 @@ const initialState: State = { walkthroughMap: new Map(), }; export default function (state = initialState, action) { - console.log('action : ', action); + // console.log('action : ', action); const { type, payload } = action; switch (type) { diff --git a/src/screens/assetDetails/screen/assetDetailsScreen.tsx b/src/screens/assetDetails/screen/assetDetailsScreen.tsx index dc6e3c5a6..3dfb8271d 100644 --- a/src/screens/assetDetails/screen/assetDetailsScreen.tsx +++ b/src/screens/assetDetails/screen/assetDetailsScreen.tsx @@ -157,8 +157,6 @@ const AssetDetailsScreen = ({ navigation, route }: AssetDetailsScreenProps) => { referredUsername: baseActivity.details?.split(' ')[2]?.slice(1), // from @user1 to @user2 initialAmount: `${parseToken(baseActivity.value)}`, initialMemo: baseActivity.memo, - initialRecurrence: `${parseToken(baseActivity.recurrence)}`, - initialExecutions: `${parseToken(baseActivity.executions)}`, }; } diff --git a/src/screens/transfer/index.js b/src/screens/transfer/index.js index 366d39624..9be85257d 100644 --- a/src/screens/transfer/index.js +++ b/src/screens/transfer/index.js @@ -30,8 +30,6 @@ const Transfer = ({ navigation, route }) => ( spkMarkets, initialAmount, initialMemo, - initialRecurrence, - initialExecutions, recurrentTransfers, fetchRecurrentTransfers, }) => { @@ -102,8 +100,6 @@ const Transfer = ({ navigation, route }) => ( referredUsername={referredUsername || ''} initialAmount={initialAmount || ''} initialMemo={initialMemo || ''} - initialRecurrence={initialRecurrence || ''} - initialExecutions={initialExecutions || ''} recurrentTransfers={recurrentTransfers || []} fetchRecurrentTransfers={fetchRecurrentTransfers} /> diff --git a/src/screens/transfer/screen/transferScreen.js b/src/screens/transfer/screen/transferScreen.js index c660e4480..93d13850d 100644 --- a/src/screens/transfer/screen/transferScreen.js +++ b/src/screens/transfer/screen/transferScreen.js @@ -26,7 +26,6 @@ import { getSpkTransactionId, SPK_NODE_ECENCY, } from '../../../providers/hive-spk/hiveSpk'; -import { RECURRENCE_TYPES } from '../../../components/transferAmountInputSection/transferAmountInputSection'; import parseToken from '../../../utils/parseToken'; const TransferView = ({ @@ -46,8 +45,6 @@ const TransferView = ({ referredUsername, initialAmount, initialMemo, - initialRecurrence, - initialExecutions, fetchRecurrentTransfers, recurrentTransfers, }) => { @@ -82,6 +79,7 @@ const TransferView = ({ ); const [recurrence, setRecurrence] = useState(''); const [executions, setExecutions] = useState(''); + const [startDate, setStartDate] = useState(''); const [isUsernameValid, setIsUsernameValid] = useState( !!( @@ -129,6 +127,19 @@ const TransferView = ({ { trailing: true }, ); + const _handleDeleteRecurrentTransfer = debounce( + () => { + setIsTransfering(true); + if (accountType === AUTH_TYPE.STEEM_CONNECT) { + setHsTransfer(true); + } else { + transferToAccount(from, destination, '0', memo, 24, 2); + } + }, + 300, + { trailing: true }, + ); + let path; if (hsTransfer) { @@ -224,7 +235,7 @@ const TransferView = ({ console.log('path is: ', path); } - const _onNextPress = () => { + const _onNextPress = (deleteTransfer = false) => { if (balance < amount) { Alert.alert(intl.formatMessage({ id: 'wallet.low_liquidity' })); @@ -240,7 +251,7 @@ const TransferView = ({ }, { text: intl.formatMessage({ id: 'alert.confirm' }), - onPress: _handleTransferAction, + onPress: deleteTransfer ? _handleDeleteRecurrentTransfer : _handleTransferAction, }, ], }), @@ -267,6 +278,7 @@ const TransferView = ({ let newMemo, newAmount, newRecurrence, + newStartDate, newExecutions = ''; if (existingRecurrentTransfer) { @@ -274,15 +286,20 @@ const TransferView = ({ newAmount = parseToken(existingRecurrentTransfer.amount).toString(); newRecurrence = existingRecurrentTransfer.recurrence.toString(); newExecutions = `${existingRecurrentTransfer.remaining_executions}`; + newStartDate = existingRecurrentTransfer.trigger_date; - setMemo(newMemo); - setAmount(newAmount); - setRecurrence(newRecurrence); - setExecutions(newExecutions); - - return existingRecurrentTransfer; + console.log('===================================='); + console.log('existingRecurrentTransfer'); + console.log('===================================='); + console.log(existingRecurrentTransfer); } - return null; + setMemo(newMemo); + setAmount(newAmount); + setRecurrence(newRecurrence); + setExecutions(newExecutions); + setStartDate(newStartDate); + + return existingRecurrentTransfer; }, [recurrentTransfers], ); @@ -338,6 +355,8 @@ const TransferView = ({ setRecurrence={setRecurrence} executions={executions} setExecutions={setExecutions} + startDate={startDate} + onNext={_onNextPress} /> { * * */ export const dateToFormatted = (d, format = 'LLLL') => { - const isTimeZoned = d.indexOf('.') !== -1 || d.indexOf('+') !== -1 ? d : `${d}.000Z`; + const isTimeZoned = d?.indexOf('.') !== -1 || d?.indexOf('+') !== -1 ? d : `${d}.000Z`; const dm = moment(new Date(isTimeZoned)); return dm.format(format); }; From 38f9e2431fac03923185532e17e058f7303731c1 Mon Sep 17 00:00:00 2001 From: gamingumar Date: Wed, 1 Nov 2023 15:03:05 +0500 Subject: [PATCH 8/8] chore: GU-2746 translation and dropdown ui update --- src/components/dropdownButton/view/dropdownButtonView.tsx | 2 +- src/config/locales/en-US.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/dropdownButton/view/dropdownButtonView.tsx b/src/components/dropdownButton/view/dropdownButtonView.tsx index b5294293d..5ee2f2990 100644 --- a/src/components/dropdownButton/view/dropdownButtonView.tsx +++ b/src/components/dropdownButton/view/dropdownButtonView.tsx @@ -78,7 +78,7 @@ const DropdownButtonView = ({ }} style={[!style ? styles.button : style]} textStyle={[textStyle || styles.buttonText]} - dropdownStyle={[styles.dropdown, dropdownStyle, { height: 32 * (options.length + 1.5) }]} + dropdownStyle={[styles.dropdown, dropdownStyle, { height: 32 * (options.length + 0.8) }]} dropdownTextStyle={[dropdownTextStyle || styles.dropdownText]} dropdownTextHighlightStyle={styles.dropdownTextHighlight} options={options} diff --git a/src/config/locales/en-US.json b/src/config/locales/en-US.json index 47f33005c..caca4e858 100644 --- a/src/config/locales/en-US.json +++ b/src/config/locales/en-US.json @@ -783,7 +783,7 @@ "to_placeholder": "Username", "memo_placeholder": "Enter your notes here", "recurrence_placeholder": "Choose Recurrence", - "executions_placeholder": "Number of Executions", + "executions_placeholder": "Number of Repeats", "transfer_token": "Transfer", "purchase_estm": "Purchase Points", "convert": "Convert HBD to HIVE",