From 2eb1cdd69d322973a77ae34b76cbb136c11758d2 Mon Sep 17 00:00:00 2001 From: bry Date: Wed, 9 Aug 2023 12:04:45 -0500 Subject: [PATCH 1/4] Asserting with gain and height + dismissable keyboard --- ios/Podfile.lock | 8 +- .../collectables/AssertLocationScreen.tsx | 167 ++++++++++-------- src/hooks/useIotInfo.ts | 10 +- src/hooks/useMobileInfo.ts | 10 +- 4 files changed, 101 insertions(+), 94 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index a85de240a..109a1773f 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -841,7 +841,7 @@ SPEC CHECKSUMS: BEMCheckBox: 5ba6e37ade3d3657b36caecc35c8b75c6c2b1a4e boost: 57d2868c099736d80fcd648bf211b4431e51a558 BVLinearGradient: 34a999fda29036898a09c6a6b728b0b4189e1a44 - Charts: ce0768268078eee0336f122c3c4ca248e4e204c5 + Charts: 354f86803d11d9c35de280587fef50d1af063978 DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 EXApplication: d8f53a7eee90a870a75656280e8d4b85726ea903 EXBarCodeScanner: 8e23fae8d267dbef9f04817833a494200f1fce35 @@ -861,9 +861,9 @@ SPEC CHECKSUMS: FBLazyVector: f1897022b53abf1469d6ad692ee2c69f57d967f3 FBReactNativeSpec: 627fd07f1b9d498c9fa572e76d7f1a6b1ee9a444 fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 - glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b + glog: 791fe035093b84822da7f0870421a25839ca7870 helium-react-native-sdk: 32c0a7e3abc733a7f3d291013b2db31475fc6980 - hermes-engine: 0784cadad14b011580615c496f77e0ae112eed75 + hermes-engine: 7a53ccac09146018a08239c5425625fdb79a6162 libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 MapboxCommon: fdf7fd31c90b7b607cd9c63e37797f023c01d860 MapboxCoreMaps: 24270c7c6b8cb71819fc2f3c549db9620ee4d019 @@ -871,7 +871,7 @@ SPEC CHECKSUMS: MapboxMobileEvents: de50b3a4de180dd129c326e09cd12c8adaaa46d6 MultiplatformBleAdapter: 5a6a897b006764392f9cef785e4360f54fb9477d OneSignalXCFramework: 81ceac017a290f23793443323090cfbe888f74ea - RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1 + RCT-Folly: 85766c3226c7ec638f05ad7cb3cf6a268d6c4241 RCTRequired: bd6045fbd511da5efe6db89eecb21e4e36bd7cbf RCTTypeSafety: c06d9f906faa69dd1c88223204c3a24767725fd8 React: b9ea33557ef1372af247f95d110fbdea114ed3b2 diff --git a/src/features/collectables/AssertLocationScreen.tsx b/src/features/collectables/AssertLocationScreen.tsx index 16b6aabc0..94afc4dc4 100644 --- a/src/features/collectables/AssertLocationScreen.tsx +++ b/src/features/collectables/AssertLocationScreen.tsx @@ -30,7 +30,12 @@ import React, { useRef, } from 'react' import { useTranslation } from 'react-i18next' -import { Alert, KeyboardAvoidingView } from 'react-native' +import { + Alert, + KeyboardAvoidingView, + Keyboard, + TouchableWithoutFeedback, +} from 'react-native' import { Config } from 'react-native-config' import { Edge } from 'react-native-safe-area-context' import 'text-encoding-polyfill' @@ -141,14 +146,16 @@ const AssertLocationScreen = () => { ]) useEffect(() => { - if (iotInfoAcc?.info?.gain) { - setGain(`${iotInfoAcc?.info?.gain / 10}`) - } + if (!elevGainVisible) { + if (iotInfoAcc?.info?.gain) { + setGain(`${iotInfoAcc?.info?.gain / 10}`) + } - if (iotInfoAcc?.info?.elevation) { - setElevation(`${iotInfoAcc?.info?.elevation}`) + if (iotInfoAcc?.info?.elevation) { + setElevation(`${iotInfoAcc?.info?.elevation}`) + } } - }, [iotInfoAcc, setGain, setElevation]) + }, [iotInfoAcc, elevGainVisible, setGain, setElevation]) const resetGain = useCallback( () => @@ -574,78 +581,86 @@ const AssertLocationScreen = () => { edges={backEdges} onClose={hideElevGain} > - - - - - {t('assertLocationScreen.antennaSetup')} - - - {t('assertLocationScreen.antennaSetupDescription')} - - - setGain(val), - multiline: true, - value: gain, - returnKeyType: 'next', - keyboardType: 'decimal-pad', - }} - /> - - Keyboard.dismiss()}> + + + + + {t('assertLocationScreen.antennaSetup')} + + + {t('assertLocationScreen.antennaSetupDescription')} + + + + + setElevation(val), - value: elevation, - keyboardType: 'decimal-pad', - }} + )}`} + textInputProps={{ + placeholder: t( + 'assertLocationScreen.elevationPlaceholder', + ), + onChangeText: setElevation, + value: elevation, + keyboardType: 'decimal-pad', + }} + /> + + + + - - - - - - + + + ) : undefined} diff --git a/src/hooks/useIotInfo.ts b/src/hooks/useIotInfo.ts index f067aa22a..dcc78fde5 100644 --- a/src/hooks/useIotInfo.ts +++ b/src/hooks/useIotInfo.ts @@ -4,8 +4,7 @@ import { iotInfoKey, rewardableEntityConfigKey, } from '@helium/helium-entity-manager-sdk' -import { useIdlAccount } from '@helium/helium-react-hooks' -import { IDL } from '@helium/idls/lib/esm/helium_entity_manager' +import { useAnchorAccount } from '@helium/helium-react-hooks' import { HeliumEntityManager } from '@helium/idls/lib/types/helium_entity_manager' import { PublicKey } from '@solana/web3.js' import { IOT_SUB_DAO_KEY } from '@utils/constants' @@ -21,11 +20,8 @@ export const useIotInfo = ( ): UseAccountState | undefined => { const [iotConfigKey] = rewardableEntityConfigKey(IOT_SUB_DAO_KEY, 'IOT') const [iotInfo] = iotInfoKey(iotConfigKey, entityKey || '') + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - return useIdlAccount( - iotInfo, - IDL as HeliumEntityManager, - type, - ) + return useAnchorAccount(iotInfo, type) } diff --git a/src/hooks/useMobileInfo.ts b/src/hooks/useMobileInfo.ts index 4cdd65706..2c30dcfdf 100644 --- a/src/hooks/useMobileInfo.ts +++ b/src/hooks/useMobileInfo.ts @@ -4,8 +4,7 @@ import { mobileInfoKey, rewardableEntityConfigKey, } from '@helium/helium-entity-manager-sdk' -import { useIdlAccount } from '@helium/helium-react-hooks' -import { IDL } from '@helium/idls/lib/esm/helium_entity_manager' +import { useAnchorAccount } from '@helium/helium-react-hooks' import { HeliumEntityManager } from '@helium/idls/lib/types/helium_entity_manager' import { PublicKey } from '@solana/web3.js' import { MOBILE_SUB_DAO_KEY } from '@utils/constants' @@ -24,11 +23,8 @@ export const useMobileInfo = ( 'MOBILE', ) const [mobileInfo] = mobileInfoKey(mobileConfigKey, entityKey || '') + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - return useIdlAccount( - mobileInfo, - IDL as HeliumEntityManager, - type, - ) + return useAnchorAccount(mobileInfo, type) } From 626e58e34a694f7c23be1c59335d53dc78413e7e Mon Sep 17 00:00:00 2001 From: bry Date: Wed, 9 Aug 2023 13:50:02 -0500 Subject: [PATCH 2/4] Setup Antenna --- .../collectables/AntennaSetupScreen.tsx | 212 ++++++++++++++++++ .../collectables/AssertLocationScreen.tsx | 2 +- .../collectables/CollectablesNavigator.tsx | 5 + .../collectables/HotspotDetailsScreen.tsx | 30 ++- .../collectables/collectablesTypes.ts | 3 + src/locales/en.ts | 9 + 6 files changed, 259 insertions(+), 2 deletions(-) create mode 100644 src/features/collectables/AntennaSetupScreen.tsx diff --git a/src/features/collectables/AntennaSetupScreen.tsx b/src/features/collectables/AntennaSetupScreen.tsx new file mode 100644 index 000000000..a0b303643 --- /dev/null +++ b/src/features/collectables/AntennaSetupScreen.tsx @@ -0,0 +1,212 @@ +import React, { useEffect, useState, useMemo, useCallback, memo } from 'react' +import BackScreen from '@components/BackScreen' +import { ReAnimatedBlurBox } from '@components/AnimatedBox' +import Box from '@components/Box' +import ButtonPressable from '@components/ButtonPressable' +import CircleLoader from '@components/CircleLoader' +import SafeAreaBox from '@components/SafeAreaBox' +import Text from '@components/Text' +import TextInput from '@components/TextInput' +import useSubmitTxn from '@hooks/useSubmitTxn' +import { RouteProp, useNavigation, useRoute } from '@react-navigation/native' +import { useTranslation } from 'react-i18next' +import { + KeyboardAvoidingView, + Keyboard, + TouchableWithoutFeedback, +} from 'react-native' +import { useEntityKey } from '@hooks/useEntityKey' +import { useIotInfo } from '@hooks/useIotInfo' +import { Edge } from 'react-native-safe-area-context' +import { CollectableStackParamList } from './collectablesTypes' +import { parseH3BNLocation } from '../../utils/h3' +import * as Logger from '../../utils/logger' +import { TabBarNavigationProp } from '../../navigation/rootTypes' + +const BUTTON_HEIGHT = 65 +type Route = RouteProp +const AntennaSetupScreen = () => { + const { t } = useTranslation() + const navigation = useNavigation() + const route = useRoute() + const { collectable } = route.params + const entityKey = useEntityKey(collectable) + const iotInfoAcc = useIotInfo(entityKey) + const safeEdges = useMemo(() => ['bottom'] as Edge[], []) + const backEdges = useMemo(() => ['top'] as Edge[], []) + const [hasSetDefaults, setHasSetDefaults] = useState(false) + const [gain, setGain] = useState() + const [elevation, setElevation] = useState() + const [updating, setUpdating] = useState(false) + const [transactionError, setTransactionError] = useState() + const { submitUpdateEntityInfo } = useSubmitTxn() + + const iotLocation = useMemo(() => { + if (!iotInfoAcc?.info?.location) { + return undefined + } + + return parseH3BNLocation(iotInfoAcc.info.location).reverse() + }, [iotInfoAcc]) + + useEffect(() => { + if (!hasSetDefaults && iotInfoAcc?.info) { + if (iotInfoAcc?.info?.gain) { + setGain(`${iotInfoAcc?.info?.gain / 10}`) + } + + if (iotInfoAcc?.info?.elevation) { + setElevation(`${iotInfoAcc?.info?.elevation}`) + } + + setHasSetDefaults(true) + } + }, [iotInfoAcc, setGain, setElevation, hasSetDefaults, setHasSetDefaults]) + + const handleUpdateElevGain = useCallback(async () => { + if (iotLocation && entityKey) { + setTransactionError(undefined) + setUpdating(true) + try { + await submitUpdateEntityInfo({ + type: 'iot', + entityKey, + lng: iotLocation[0], + lat: iotLocation[1], + elevation, + decimalGain: gain, + }) + setUpdating(false) + navigation.reset({ + index: 0, + routes: [{ name: 'Collectables' }], + }) + } catch (error) { + setUpdating(false) + Logger.error(error) + setTransactionError((error as Error).message) + } + } + }, [ + iotLocation, + entityKey, + elevation, + gain, + setUpdating, + setTransactionError, + submitUpdateEntityInfo, + navigation, + ]) + + const showError = useMemo(() => { + if (transactionError) return transactionError + }, [transactionError]) + + return ( + + + Keyboard.dismiss()}> + + + + + {t('antennaSetupScreen.antennaSetup')} + + + {t('antennaSetupScreen.antennaSetupDescription')} + + + + + + + + + {showError && ( + + {showError} + + )} + + + + ) : undefined + } + /> + + + + + + + ) +} + +export default memo(AntennaSetupScreen) diff --git a/src/features/collectables/AssertLocationScreen.tsx b/src/features/collectables/AssertLocationScreen.tsx index 94afc4dc4..a0ab3ce7e 100644 --- a/src/features/collectables/AssertLocationScreen.tsx +++ b/src/features/collectables/AssertLocationScreen.tsx @@ -558,7 +558,7 @@ const AssertLocationScreen = () => { } TrailingComponent={ asserting ? ( - + ) : undefined } /> diff --git a/src/features/collectables/CollectablesNavigator.tsx b/src/features/collectables/CollectablesNavigator.tsx index 5d403de6d..a7bcd1007 100644 --- a/src/features/collectables/CollectablesNavigator.tsx +++ b/src/features/collectables/CollectablesNavigator.tsx @@ -19,6 +19,7 @@ import ClaimAllRewardsScreen from './ClaimAllRewardsScreen' import ClaimingRewardsScreen from './ClaimingRewardsScreen' import CollectionScreen from './CollectionScreen' import NftDetailsScreen from './NftDetailsScreen' +import AntennaSetupScreen from './AntennaSetupScreen' const CollectablesStack = createStackNavigator() @@ -41,6 +42,10 @@ const CollectablesStackScreen = () => { name="AssertLocationScreen" component={AssertLocationScreen} /> + { const copyText = useCopyText() const { collectable } = route.params + const entityKey = useEntityKey(collectable) + const iotInfoAcc = useIotInfo(entityKey) + const pendingIotRewards = collectable && collectable.pendingRewards && @@ -75,6 +80,13 @@ const HotspotDetailsScreen = () => { }) }, [collectable, navigation]) + const handleAntennaSetup = useCallback(() => { + setOptionsOpen(false) + navigation.navigate('AntennaSetupScreen', { + collectable, + }) + }, [collectable, navigation]) + const handleClaimRewards = useCallback(() => { navigation.navigate('ClaimRewardsScreen', { hotspot: collectable, @@ -123,6 +135,15 @@ const HotspotDetailsScreen = () => { selected={false} hasPressedState={false} /> + {iotInfoAcc?.info?.location && ( + + )} { /> ), - [handleSend, handleAssertLocation, handleCopyAddress, t], + [ + handleSend, + handleAssertLocation, + handleAntennaSetup, + handleCopyAddress, + iotInfoAcc, + t, + ], ) return ( diff --git a/src/features/collectables/collectablesTypes.ts b/src/features/collectables/collectablesTypes.ts index 19a499c71..fa15c59d3 100644 --- a/src/features/collectables/collectablesTypes.ts +++ b/src/features/collectables/collectablesTypes.ts @@ -18,6 +18,9 @@ export type CollectableStackParamList = { AssertLocationScreen: { collectable: HotspotWithPendingRewards } + AntennaSetupScreen: { + collectable: HotspotWithPendingRewards + } PaymentScreen: undefined | PaymentRouteParam ClaimRewardsScreen: { diff --git a/src/locales/en.ts b/src/locales/en.ts index 4aaaa8184..17cdd9b1d 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -324,6 +324,15 @@ export default { locationNotFound: 'Location not found, Please try again.', mobileTitle: 'MOBILE', }, + antennaSetupScreen: { + title: 'Antenna Setup', + antennaSetup: 'Antenna Setup', + antennaSetupDescription: + 'Submit gain and elevation details for your Hotspot', + gainPlaceholder: 'TX / RX Gain (dBi)', + elevationPlaceholder: 'Elevation (meters)', + submit: 'Update Antenna', + }, swapsScreen: { title: 'Swap my Tokens', swapTokens: 'Swap Tokens', From 13b05b0fa7ce0a014a01a6322d27f315397f5833 Mon Sep 17 00:00:00 2001 From: bry Date: Wed, 9 Aug 2023 15:16:21 -0500 Subject: [PATCH 3/4] PR feedback --- src/hooks/useIotInfo.ts | 5 +---- src/hooks/useMobileInfo.ts | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/hooks/useIotInfo.ts b/src/hooks/useIotInfo.ts index dcc78fde5..4a605a942 100644 --- a/src/hooks/useIotInfo.ts +++ b/src/hooks/useIotInfo.ts @@ -1,5 +1,4 @@ import { IdlAccounts } from '@coral-xyz/anchor' -import { UseAccountState } from '@helium/account-fetch-cache-hooks' import { iotInfoKey, rewardableEntityConfigKey, @@ -15,9 +14,7 @@ export type IotHotspotInfoV0 = pubKey: PublicKey } -export const useIotInfo = ( - entityKey: string | undefined, -): UseAccountState | undefined => { +export const useIotInfo = (entityKey: string | undefined) => { const [iotConfigKey] = rewardableEntityConfigKey(IOT_SUB_DAO_KEY, 'IOT') const [iotInfo] = iotInfoKey(iotConfigKey, entityKey || '') diff --git a/src/hooks/useMobileInfo.ts b/src/hooks/useMobileInfo.ts index 2c30dcfdf..40272d8d5 100644 --- a/src/hooks/useMobileInfo.ts +++ b/src/hooks/useMobileInfo.ts @@ -1,5 +1,4 @@ import { IdlAccounts } from '@coral-xyz/anchor' -import { UseAccountState } from '@helium/account-fetch-cache-hooks' import { mobileInfoKey, rewardableEntityConfigKey, @@ -15,9 +14,7 @@ export type MobileHotspotInfoV0 = pubKey: PublicKey } -export const useMobileInfo = ( - entityKey: string | undefined, -): UseAccountState | undefined => { +export const useMobileInfo = (entityKey: string | undefined) => { const [mobileConfigKey] = rewardableEntityConfigKey( MOBILE_SUB_DAO_KEY, 'MOBILE', From b11628f5c86a572b12fd68ef5dc04879e80f65a3 Mon Sep 17 00:00:00 2001 From: bry Date: Wed, 9 Aug 2023 16:31:41 -0500 Subject: [PATCH 4/4] Add setting up antenna screen --- .../collectables/AntennaSetupScreen.tsx | 28 +-- .../collectables/CollectablesNavigator.tsx | 5 + .../collectables/SettingUpAntennaScreen.tsx | 196 ++++++++++++++++++ .../collectables/collectablesTypes.ts | 4 +- src/locales/en.ts | 6 + 5 files changed, 219 insertions(+), 20 deletions(-) create mode 100644 src/features/collectables/SettingUpAntennaScreen.tsx diff --git a/src/features/collectables/AntennaSetupScreen.tsx b/src/features/collectables/AntennaSetupScreen.tsx index a0b303643..cd9911835 100644 --- a/src/features/collectables/AntennaSetupScreen.tsx +++ b/src/features/collectables/AntennaSetupScreen.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState, useMemo, useCallback, memo } from 'react' import BackScreen from '@components/BackScreen' -import { ReAnimatedBlurBox } from '@components/AnimatedBox' +import { ReAnimatedBox } from '@components/AnimatedBox' import Box from '@components/Box' import ButtonPressable from '@components/ButtonPressable' import CircleLoader from '@components/CircleLoader' @@ -18,16 +18,19 @@ import { import { useEntityKey } from '@hooks/useEntityKey' import { useIotInfo } from '@hooks/useIotInfo' import { Edge } from 'react-native-safe-area-context' -import { CollectableStackParamList } from './collectablesTypes' +import { DelayedFadeIn } from '@components/FadeInOut' +import { + CollectableNavigationProp, + CollectableStackParamList, +} from './collectablesTypes' import { parseH3BNLocation } from '../../utils/h3' import * as Logger from '../../utils/logger' -import { TabBarNavigationProp } from '../../navigation/rootTypes' const BUTTON_HEIGHT = 65 type Route = RouteProp const AntennaSetupScreen = () => { const { t } = useTranslation() - const navigation = useNavigation() + const nav = useNavigation() const route = useRoute() const { collectable } = route.params const entityKey = useEntityKey(collectable) @@ -76,11 +79,7 @@ const AntennaSetupScreen = () => { elevation, decimalGain: gain, }) - setUpdating(false) - navigation.reset({ - index: 0, - routes: [{ name: 'Collectables' }], - }) + nav.push('SettingUpAntennaScreen') } catch (error) { setUpdating(false) Logger.error(error) @@ -95,7 +94,7 @@ const AntennaSetupScreen = () => { setUpdating, setTransactionError, submitUpdateEntityInfo, - navigation, + nav, ]) const showError = useMemo(() => { @@ -103,12 +102,7 @@ const AntennaSetupScreen = () => { }, [transactionError]) return ( - + { - + ) } diff --git a/src/features/collectables/CollectablesNavigator.tsx b/src/features/collectables/CollectablesNavigator.tsx index a7bcd1007..104becc5c 100644 --- a/src/features/collectables/CollectablesNavigator.tsx +++ b/src/features/collectables/CollectablesNavigator.tsx @@ -20,6 +20,7 @@ import ClaimingRewardsScreen from './ClaimingRewardsScreen' import CollectionScreen from './CollectionScreen' import NftDetailsScreen from './NftDetailsScreen' import AntennaSetupScreen from './AntennaSetupScreen' +import SettingUpAntennaScreen from './SettingUpAntennaScreen' const CollectablesStack = createStackNavigator() @@ -46,6 +47,10 @@ const CollectablesStackScreen = () => { name="AntennaSetupScreen" component={AntennaSetupScreen} /> + { + const { currentAccount } = useAccountStorage() + const navigation = useNavigation() + const wallet = useCurrentWallet() + const solBalance = useBN(useSolOwnedAmount(wallet).amount) + const { bottom } = useSafeAreaInsets() + + const { t } = useTranslation() + const solanaPayment = useSelector( + (reduxState: RootState) => reduxState.solana.payment, + ) + + const onReturn = useCallback(() => { + // Reset Collectables stack to first screen + navigation.reset({ + index: 0, + routes: [{ name: 'Collectables' }], + }) + }, [navigation]) + + if (!currentAccount) { + return null + } + + return ( + + + + + + + {solanaPayment && !solanaPayment.error && !solanaPayment.loading && ( + + + {t('antennaSetupScreen.settingUpComplete')} + + + {t('antennaSetupScreen.settingUpCompleteBody')} + + + )} + + {solanaPayment?.error && ( + + + {t('collectablesScreen.rewardsError')} + + + {parseTransactionError( + solBalance, + solanaPayment?.error?.message, + )} + + + )} + + {!solanaPayment && ( + + + {t('antennaSetupScreen.settingUpError')} + + + )} + + {solanaPayment && solanaPayment.loading && ( + + + {t('antennaSetupScreen.settingUp')} + + + {t('antennaSetupScreen.settingUpBody')} + + + + + + )} + + + + } + /> + + + + ) +} + +export default memo(SettingUpAntennaScreen) diff --git a/src/features/collectables/collectablesTypes.ts b/src/features/collectables/collectablesTypes.ts index fa15c59d3..0b4e65a8b 100644 --- a/src/features/collectables/collectablesTypes.ts +++ b/src/features/collectables/collectablesTypes.ts @@ -21,14 +21,13 @@ export type CollectableStackParamList = { AntennaSetupScreen: { collectable: HotspotWithPendingRewards } + SettingUpAntennaScreen: undefined PaymentScreen: undefined | PaymentRouteParam - ClaimRewardsScreen: { hotspot: HotspotWithPendingRewards } ClaimAllRewardsScreen: undefined ClaimingRewardsScreen: undefined - CollectionScreen: { collection: Collectable[] } @@ -44,7 +43,6 @@ export type CollectableStackParamList = { TransferCompleteScreen: { collectable: CompressedNFT | Collectable } - AddNewContact: undefined PaymentQrScanner: undefined AddressBookNavigator: undefined diff --git a/src/locales/en.ts b/src/locales/en.ts index 17cdd9b1d..c6b1a3d4e 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -332,6 +332,12 @@ export default { gainPlaceholder: 'TX / RX Gain (dBi)', elevationPlaceholder: 'Elevation (meters)', submit: 'Update Antenna', + settingUp: 'Setting up your antenna...', + settingUpBody: 'Please wait while we update your Antenna!', + settingUpError: 'Antenna Setup failed. Please try again later.', + settingUpComplete: 'Antenna Setup!', + settingUpCompleteBody: + 'We’ve updated the gain and elevation of your antenna.', }, swapsScreen: { title: 'Swap my Tokens',