Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show hotspot location in list and detail screen. #467

Merged
merged 1 commit into from
Sep 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/features/collectables/AntennaSetupScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const AntennaSetupScreen = () => {
return undefined
}

return parseH3BNLocation(iotInfoAcc.info.location).reverse()
return parseH3BNLocation(iotInfoAcc.info.location)?.reverse()
}, [iotInfoAcc])

useEffect(() => {
Expand Down
4 changes: 2 additions & 2 deletions src/features/collectables/AssertLocationScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,15 @@ const AssertLocationScreen = () => {
return undefined
}

return parseH3BNLocation(iotInfoAcc.info.location).reverse()
return parseH3BNLocation(iotInfoAcc.info.location)?.reverse()
}, [iotInfoAcc])

const mobileLocation = useMemo(() => {
if (!mobileInfoAcc?.info?.location) {
return undefined
}

return parseH3BNLocation(mobileInfoAcc.info.location).reverse()
return parseH3BNLocation(mobileInfoAcc.info.location)?.reverse()
}, [mobileInfoAcc])

const sameLocation = useMemo(() => {
Expand Down
56 changes: 38 additions & 18 deletions src/features/collectables/HotspotCompressedListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { useMint } from '@helium/helium-react-hooks'
import { Mints } from '../../utils/constants'
import { removeDashAndCapitalize } from '../../utils/hotspotNftsUtils'
import { HotspotWithPendingRewards } from '../../types/solana'
import useHotspotLocation from '../../hooks/useHotspotLocation'

export type HotspotListItemProps = {
hotspot: HotspotWithPendingRewards
Expand All @@ -32,6 +33,17 @@ const HotspotListItem = ({
content: { metadata },
} = hotspot

const eccCompact = useMemo(() => {
if (!metadata || !metadata?.attributes?.length) {
return undefined
}

return metadata.attributes.find((attr) => attr.trait_type === 'ecc_compact')
?.value
}, [metadata])

const streetAddress = useHotspotLocation(eccCompact)

const { info: iotMint } = useMint(IOT_MINT)
const { info: mobileMint } = useMint(MOBILE_MINT)

Expand Down Expand Up @@ -65,15 +77,6 @@ const HotspotListItem = ({
return formatLargeNumber(new BigNumber(num))
}, [hotspot, mobileMint])

const eccCompact = useMemo(() => {
if (!metadata || !metadata?.attributes?.length) {
return undefined
}

return metadata.attributes.find((attr) => attr.trait_type === 'ecc_compact')
?.value
}, [metadata])

const hasIotRewards = useMemo(
() => pendingIotRewards && pendingIotRewards.gt(new BN(0)),
[pendingIotRewards],
Expand Down Expand Up @@ -102,22 +105,39 @@ const HotspotListItem = ({
cache: 'force-cache',
}}
/>
<Box marginStart="m" marginVertical="s" flex={1}>
<Box
marginStart="m"
marginVertical="s"
flex={1}
justifyContent="center"
>
{metadata?.name && (
<Text textAlign="left" variant="subtitle2" adjustsFontSizeToFit>
<Text
textAlign="left"
variant="subtitle2"
numberOfLines={1}
adjustsFontSizeToFit
>
{removeDashAndCapitalize(metadata.name)}
</Text>
)}

<Box flexGrow={1} />

<Box flexDirection="row" marginEnd="s" alignItems="flex-end">
<Text variant="subtitle3" color="secondaryText">
{eccCompact ? ellipsizeAddress(eccCompact) : ''}
{streetAddress && (
<Text variant="body2" numberOfLines={1} adjustsFontSizeToFit>
{streetAddress}
</Text>
</Box>
)}

<Text
variant="subtitle3"
color="secondaryText"
numberOfLines={1}
adjustsFontSizeToFit
>
{eccCompact ? ellipsizeAddress(eccCompact) : ''}
</Text>
</Box>
<Box marginVertical="s" marginEnd="s">
<Box marginVertical="s" marginHorizontal="s">
{!!hasMobileRewards && (
<Box
marginBottom="s"
Expand Down
17 changes: 16 additions & 1 deletion src/features/collectables/HotspotDetailsScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import { ellipsizeAddress } from '@utils/accountUtils'
import { toNumber } from '@helium/spl-utils'
import { useEntityKey } from '@hooks/useEntityKey'
import { useIotInfo } from '@hooks/useIotInfo'
import { FadeIn } from 'react-native-reanimated'
import useHotspotLocation from '@hooks/useHotspotLocation'
import { ww } from '../../utils/layout'
import {
CollectableNavigationProp,
Expand All @@ -47,6 +49,7 @@ const HotspotDetailsScreen = () => {
const { collectable } = route.params
const entityKey = useEntityKey(collectable)
const iotInfoAcc = useIotInfo(entityKey)
const streetAddress = useHotspotLocation(entityKey)

const pendingIotRewards =
collectable &&
Expand Down Expand Up @@ -211,7 +214,19 @@ const HotspotDetailsScreen = () => {
}}
/>
</Box>
<Box marginTop="l" marginBottom="m">
{streetAddress && (
<ReAnimatedBox entering={FadeIn}>
<Text variant="body1" marginTop="l" textAlign="center">
{streetAddress || ' '}
</Text>
</ReAnimatedBox>
)}
{!streetAddress && (
<Text variant="body1" marginTop="l" textAlign="center">
{' '}
</Text>
)}
<Box marginTop="m">
<Text variant="body1" marginBottom="ms">
{t('collectablesScreen.hotspots.pendingRewardsTitle')}
</Text>
Expand Down
9 changes: 5 additions & 4 deletions src/features/collectables/HotspotList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ import { formatLargeNumber } from '../../utils/accountUtils'
import HotspotCompressedListItem from './HotspotCompressedListItem'
import HotspotListItem from './HotspotListItem'
import { NFTSkeleton } from './NftListItem'
import { CollectableNavigationProp } from './collectablesTypes'

export const DEFAULT_PAGE_AMOUNT = 20
import {
CollectableNavigationProp,
DEFAULT_PAGE_AMOUNT,
} from './collectablesTypes'

function RewardItem({
mint,
Expand Down Expand Up @@ -301,7 +302,7 @@ const HotspotList = () => {
hotspot={item}
onPress={handleNavigateToCollectable}
key={item.id}
marginBottom="xs"
marginBottom="s"
/>
)
}
Expand Down
2 changes: 2 additions & 0 deletions src/features/collectables/collectablesTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,5 @@ export type CollectableStackParamList = {

export type CollectableNavigationProp =
StackNavigationProp<CollectableStackParamList>

export const DEFAULT_PAGE_AMOUNT = 20
79 changes: 79 additions & 0 deletions src/hooks/useHotspotLocation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { useEffect, useMemo } from 'react'
import { useSelector } from 'react-redux'
import {
iotInfoKey,
mobileInfoKey,
rewardableEntityConfigKey,
} from '@helium/helium-entity-manager-sdk'
import { useAsync } from 'react-async-hook'
import { useAppDispatch } from '../store/store'
import { getGeocodedAddress } from '../store/slices/locationSlice'
import { RootState } from '../store/rootReducer'
import { useSolana } from '../solana/SolanaProvider'
import { IOT_SUB_DAO_KEY, MOBILE_SUB_DAO_KEY } from '../utils/constants'

const useHotspotLocation = (entityKey?: string) => {
const dispatch = useAppDispatch()
const { locations } = useSelector((state: RootState) => state.location)
const { hemProgram } = useSolana()

const { result: location } = useAsync(async () => {
const [iotConfigKey] = rewardableEntityConfigKey(IOT_SUB_DAO_KEY, 'IOT')
const [iotKey] = iotInfoKey(iotConfigKey, entityKey || '')
const iotInfo = await hemProgram?.account.iotHotspotInfoV0.fetch(iotKey)
if (iotInfo?.location) {
const loc = iotInfo.location
if (loc && loc.toString('hex') !== '0') {
return loc
}
}

const [mobileConfigKey] = rewardableEntityConfigKey(
MOBILE_SUB_DAO_KEY,
'MOBILE',
)
const [mobileKey] = mobileInfoKey(mobileConfigKey, entityKey || '')
const mobileInfo = await hemProgram?.account.mobileHotspotInfoV0.fetch(
mobileKey,
)

if (mobileInfo?.location) {
const loc = mobileInfo.location
if (loc && loc.toString('hex') !== '0') {
return loc
}
}
}, [])

useEffect(() => {
if (!entityKey || !location) return
const geo = locations[location?.toString('hex') || '']
if (geo) return

dispatch(getGeocodedAddress({ location }))
}, [dispatch, entityKey, location, locations])

const streetAddress = useMemo(() => {
const geo = locations[location?.toString('hex') || '']
if (!geo || !geo.features?.length) return ''

const feature = geo.features[0]

const place = feature.context?.find((c) => c.id.includes('place'))
const region = feature.context?.find((c) => c.id.includes('region'))

if (place && region) {
return `${place.text}, ${region.text}`
}

if (place) {
return place
}

return region
}, [location, locations])

return streetAddress
}

export default useHotspotLocation
57 changes: 52 additions & 5 deletions src/hooks/useReverseGeo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@ import { useAsync } from 'react-async-hook'

export const useReverseGeo = (coords: number[] | undefined) =>
useAsync(async () => {
if (!coords) return ''

const response = await axios.get(
`https://api.mapbox.com/geocoding/v5/mapbox.places/${coords[0]},${coords[1]}.json?access_token=${Config.MAPBOX_ACCESS_TOKEN}`,
)
const response = await reverseGeoCode(coords)
if (!response) return ''

const placeName = response.data.features[0].place_name
const parts = placeName.split(',')
Expand All @@ -18,3 +15,53 @@ export const useReverseGeo = (coords: number[] | undefined) =>

return `${address}, ${city}, ${state}`
}, [coords])

export const reverseGeoCode = async (coords: number[] | undefined) => {
if (!coords) return

return axios.get<undefined, { data: Geo }>(
`https://api.mapbox.com/geocoding/v5/mapbox.places/${coords[0]},${coords[1]}.json?access_token=${Config.MAPBOX_ACCESS_TOKEN}`,
)
}

export interface Geo {
type: string
query: number[]
features: Feature[]
attribution: string
}

export interface Feature {
id: string
type: string
place_type: string[]
relevance: number
properties: Properties
text: string
place_name: string
center: number[]
geometry: Geometry
address?: string
context?: Context[]
bbox?: number[]
}

export interface Properties {
accuracy?: string
mapbox_id: string
wikidata?: string
short_code?: string
}

export interface Geometry {
type: string
coordinates: number[]
}

export interface Context {
id: string
mapbox_id: string
text: string
wikidata?: string
short_code?: string
}
4 changes: 4 additions & 0 deletions src/store/rootReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import authReducer, { name as authSliceName } from './slices/authSlice'
import hotspotReducer, {
name as hotspotSliceName,
} from './slices/hotspotsSlice'
import locationReducer, {
name as locationSliceName,
} from './slices/locationSlice'
import browserReducer, { name as browserSliceName } from './slices/browserSlice'

const solanaConfig = {
Expand Down Expand Up @@ -53,6 +56,7 @@ const reducer = combineReducers({
[appSliceName]: appReducer,
[hotspotSliceName]: hotspotReducer,
[browserSliceName]: browserReducer,
[locationSliceName]: locationReducer,
})

export const rootReducer = (state: RootState, action: AnyAction) => {
Expand Down
2 changes: 1 addition & 1 deletion src/store/slices/hotspotsSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { AnchorProvider } from '@coral-xyz/anchor'
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'
import { Cluster, PublicKey } from '@solana/web3.js'
import { CompressedNFT, HotspotWithPendingRewards } from 'src/types/solana'
import { DEFAULT_PAGE_AMOUNT } from '../../features/collectables/HotspotList'
import { CSAccount } from '../../storage/cloudStorage'
import * as solUtils from '../../utils/solanaUtils'
import { DEFAULT_PAGE_AMOUNT } from '../../features/collectables/collectablesTypes'

export type WalletHotspots = {
hotspots: CompressedNFT[]
Expand Down
Loading