Skip to content

Commit

Permalink
CP-9945: Add Trending Tokens (#2325)
Browse files Browse the repository at this point in the history
  • Loading branch information
atn4z7 authored Feb 27, 2025
1 parent a3bf28e commit 9e3459d
Show file tree
Hide file tree
Showing 49 changed files with 1,263 additions and 562 deletions.
8 changes: 5 additions & 3 deletions packages/core-mobile/app/components/Avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { TokenSymbol } from 'store/network'
import { SvgUri } from 'react-native-svg'
import { formatUriImageToPng, isContentfulImageUri } from 'utils/Contentful'
import { Image } from 'expo-image'
import { Text, useTheme, View } from '@avalabs/k2-mobile'
import { Text, useTheme, View, alpha } from '@avalabs/k2-mobile'
import { useGetInitials } from 'hooks/useGetInitials'
import { SuggestedSiteName } from 'store/browser/const'
import { isBase64Png, isSugguestedSiteName } from 'screens/browser/utils'
Expand Down Expand Up @@ -143,6 +143,7 @@ interface TokenAvatarProps {
}

const TokenAvatar: FC<TokenAvatarProps> = props => {
const { theme } = useTheme()
return (
<AvatarBase
{...props}
Expand All @@ -151,6 +152,7 @@ const TokenAvatar: FC<TokenAvatarProps> = props => {
testID={props.symbol}
backgroundColor={props.backgroundColor}
showBorder={props.showBorder}
fallbackBackgroundColor={alpha(theme.colors.$neutral700, 0.5)}
/>
)
}
Expand Down Expand Up @@ -208,8 +210,8 @@ function FallbackAvatar({
variant="body1"
sx={{
color: '$neutral50',
fontSize: size * 0.5,
lineHeight: size * 0.75
fontSize: size * 0.4,
lineHeight: size * 0.65
}}>
{initials}
</Text>
Expand Down
4 changes: 2 additions & 2 deletions packages/core-mobile/app/components/PortfolioListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ const PortfolioListItem: FC<Props> = ({
/>
)

const { getMarketToken } = useWatchlist()
const marketToken = getMarketToken(symbol)
const { getMarketTokenBySymbol } = useWatchlist()
const marketToken = getMarketTokenBySymbol(symbol)
const percentChange = marketToken?.priceChangePercentage24h ?? undefined
const priceChange = percentChange
? (tokenPriceInCurrency * percentChange) / 100
Expand Down
45 changes: 29 additions & 16 deletions packages/core-mobile/app/components/ZeroState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ interface BaseProps {
const ZeroStateBase: FC<BaseProps> = ({ image, title, message, button }) => {
const { theme } = useApplicationContext()

function renderImage() {
function renderImage(): JSX.Element | null {
if (!image) {
return null
}
Expand All @@ -41,7 +41,7 @@ const ZeroStateBase: FC<BaseProps> = ({ image, title, message, button }) => {
)
}

function renderTitle() {
function renderTitle(): JSX.Element {
if (typeof title === 'string') {
return (
<AvaText.Heading2 textStyle={{ marginTop: 16 }}>
Expand All @@ -52,7 +52,7 @@ const ZeroStateBase: FC<BaseProps> = ({ image, title, message, button }) => {
return <View style={{ marginTop: 16 }}>{title}</View>
}

function renderMessage() {
function renderMessage(): JSX.Element {
if (typeof message === 'string') {
return (
<AvaText.Body2
Expand All @@ -64,7 +64,7 @@ const ZeroStateBase: FC<BaseProps> = ({ image, title, message, button }) => {
return <View>{message}</View>
}

function renderButton() {
function renderButton(): JSX.Element | null {
if (!button) return null

return (
Expand Down Expand Up @@ -93,7 +93,11 @@ const ZeroStateBase: FC<BaseProps> = ({ image, title, message, button }) => {
)
}

function ZeroStateNetworkTokens({ goToReceive }: { goToReceive: () => void }) {
function ZeroStateNetworkTokens({
goToReceive
}: {
goToReceive: () => void
}): JSX.Element {
const title = 'No assets'
const message = 'Add assets by clicking the button below.'

Expand All @@ -109,14 +113,14 @@ function ZeroStateNetworkTokens({ goToReceive }: { goToReceive: () => void }) {
return <ZeroStateBase title={title} message={message} button={button} />
}

function ZeroStateCollectibles() {
function ZeroStateCollectibles(): JSX.Element {
const title = 'No Collectibles'
const message = 'You don’t have any collectibles yet.'

return <ZeroStateBase title={title} message={message} />
}

function ZeroStateNoRecentAccounts() {
function ZeroStateNoRecentAccounts(): JSX.Element {
const title = 'No recent recipients'
const message = 'Enter the address in the field above.'

Expand All @@ -127,7 +131,7 @@ function ZeroStateEmptyAddressBook({
onGoToAddressBook
}: {
onGoToAddressBook: () => void
}) {
}): JSX.Element {
const title = 'No addresses'
const message = 'You can add addresses in Address Book'

Expand All @@ -146,17 +150,21 @@ function ZeroStateEmptyAddressBook({

type NoResultsProps = Pick<BaseProps, 'message'>

// removed "man with lantern" as per ux request
function ZeroStateNoResults({ message }: NoResultsProps) {
function ZeroStateNoResults({ message }: NoResultsProps): JSX.Element {
const title = 'No results found'
return <ZeroStateBase title={title} message={message} />
}

function ZeroStateComingSoon() {
function ZeroStateSomethingWentWrong(): JSX.Element {
const title = 'Oops! Something went wrong'
return <ZeroStateBase title={title} message={'Please try again later'} />
}

function ZeroStateComingSoon(): JSX.Element {
return <ZeroStateBase title={'Coming soon!'} />
}

function ZeroStateNoTransactions() {
function ZeroStateNoTransactions(): JSX.Element {
const title = 'No recent activity'
const message = 'New transactions will show here'

Expand All @@ -167,7 +175,7 @@ function ZeroStateNoWatchlistFavorites({
exploreAllTokens
}: {
exploreAllTokens?: () => void
}) {
}): JSX.Element {
const title = 'No Favorites'
const message = 'Click the star icon on any token to mark it as a favorite.'

Expand All @@ -185,7 +193,11 @@ function ZeroStateNoWatchlistFavorites({
)
}

function ZeroStateNoContacts({ addContact }: { addContact: () => void }) {
function ZeroStateNoContacts({
addContact
}: {
addContact: () => void
}): JSX.Element {
const title = 'No Addresses Saved'
const message = 'Tap the button below to add an address.'
const button = (
Expand All @@ -210,7 +222,7 @@ function ZeroStateSites({
onAddNewConnection
}: {
onAddNewConnection: () => void
}) {
}): JSX.Element {
const title = 'No Connected Sites'
const message = 'Tap the button below to scan QR code and connect.'

Expand Down Expand Up @@ -243,7 +255,8 @@ const ZeroState = {
NoWatchlistFavorites: ZeroStateNoWatchlistFavorites,
EmptyAddressBook: ZeroStateEmptyAddressBook, // used in Send screens
NoContacts: ZeroStateNoContacts, // used in Contacts screen
Sites: ZeroStateSites
Sites: ZeroStateSites,
SomethingWentWrong: ZeroStateSomethingWentWrong
}

export default ZeroState
1 change: 1 addition & 0 deletions packages/core-mobile/app/consts/reactQueryKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export enum ReactQueryKeys {
NETWORKS = 'networks',
NETWORK_CONTRACT_TOKENS = 'networkContractTokens',
WATCHLIST_TOKENS_AND_CHARTS = 'watchlistTokensAndCharts',
WATCHLIST_TRENDING_TOKENS_AND_CHARTS = 'watchlistTrendingTokensAndCharts',
WATCHLIST_PRICES = 'watchlistPrices',
WATCHLIST_TOKEN_SEARCH = 'watchlistTokenSearch'
}
43 changes: 43 additions & 0 deletions packages/core-mobile/app/contexts/WatchlistContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React, {
createContext,
ReactNode,
useContext,
useMemo,
useState,
Dispatch
} from 'react'

interface WatchlistContextState {
searchText: string
setSearchText: Dispatch<string>
}

export const WatchlistContext = createContext<WatchlistContextState>(
{} as WatchlistContextState
)

export const WatchlistContextProvider = ({
children
}: {
children: ReactNode
}): JSX.Element => {
const [searchText, setSearchText] = useState('')

const state: WatchlistContextState = useMemo(
() => ({
searchText,
setSearchText
}),
[searchText, setSearchText]
)

return (
<WatchlistContext.Provider value={state}>
{children}
</WatchlistContext.Provider>
)
}

export function useWatchlistContext(): WatchlistContextState {
return useContext(WatchlistContext)
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { LocalTokenWithBalance } from 'store/balance/types'
import { MarketToken } from 'store/watchlist'
import { MarketToken } from 'store/watchlist/types'
import { useWatchlist } from '../watchlist/useWatchlist'

export const useTokenPortfolioPriceChange = (
tokens: LocalTokenWithBalance[]
): { tokenPortfolioPriceChange: number } => {
const { getMarketToken } = useWatchlist()
const { getMarketTokenBySymbol } = useWatchlist()

const tokensWithPrices = tokens
.map(token => {
const tokenInWatchlist = getMarketToken(token.symbol)
const tokenInWatchlist = getMarketTokenBySymbol(token.symbol)

if (tokenInWatchlist) {
return { ...token, ...tokenInWatchlist }
Expand Down
20 changes: 20 additions & 0 deletions packages/core-mobile/app/hooks/watchlist/useGetTrendingTokens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { UseQueryResult, useQuery } from '@tanstack/react-query'
import { ReactQueryKeys } from 'consts/reactQueryKeys'
import { TrendingToken } from 'services/token/types'
import WatchlistService from 'services/watchlist/WatchlistService'

export const useGetTrendingTokens = <TData = TrendingToken[]>(
select?: (data: TrendingToken[]) => TData
): UseQueryResult<TData, Error> => {
return useQuery({
queryKey: [ReactQueryKeys.WATCHLIST_TRENDING_TOKENS_AND_CHARTS],
queryFn: async () => WatchlistService.getTrendingTokens(),
refetchInterval: 120000, // 2 mins
select
})
}

export const useGetTrendingToken = (
address: string
): UseQueryResult<TrendingToken | undefined, Error> =>
useGetTrendingTokens(data => data.find(token => token.address === address))
Loading

0 comments on commit 9e3459d

Please sign in to comment.