Skip to content

Commit a8ae3a9

Browse files
Change home screen styling
1 parent 218c751 commit a8ae3a9

File tree

8 files changed

+411
-228
lines changed

8 files changed

+411
-228
lines changed

packages/app/components/TopNav.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ export function TopNav({
162162
)
163163
case media.gtLg && !showOnGtLg:
164164
return null
165-
case Boolean(queryParams.token):
165+
case queryParams.token !== undefined:
166166
return (
167167
<XStack ai="center" f={1}>
168168
<Button onPress={handleBack}>
@@ -174,6 +174,18 @@ export function TopNav({
174174
/>
175175
</ButtonOg.Icon>
176176
</Button>
177+
<Paragraph size={'$8'} col={'$color10'}>
178+
{(() => {
179+
switch (queryParams.token) {
180+
case 'investments':
181+
return 'Invest'
182+
case 'stables':
183+
return 'Cash'
184+
default:
185+
return 'Balance'
186+
}
187+
})()}
188+
</Paragraph>
177189
</XStack>
178190
)
179191
case !isSubRoute:

packages/app/data/coins.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ export const allCoinsDict = allCoins.reduce((acc, coin) => {
198198

199199
export type allCoinsDict = typeof allCoinsDict
200200

201-
export type CoinWithBalance = allCoins[number] & {
201+
export type CoinWithBalance = coin & {
202202
balance: bigint | undefined
203203
}
204204

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { Card, type CardProps, Paragraph, XStack } from '@my/ui'
2+
3+
import { ChevronRight } from '@tamagui/lucide-icons'
4+
import { type LinkProps, useLink } from 'solito/link'
5+
6+
export const FriendsCard = ({ href, ...props }: Omit<CardProps & LinkProps, 'children'>) => {
7+
const linkProps = useLink({ href })
8+
9+
return (
10+
<Card
11+
elevate
12+
hoverStyle={{ scale: 0.925 }}
13+
pressStyle={{ scale: 0.875 }}
14+
animation="bouncy"
15+
size={'$5'}
16+
br="$7"
17+
{...linkProps}
18+
{...props}
19+
>
20+
<Card.Header padded fd="row" ai="center" jc="space-between">
21+
<Paragraph fontSize={'$7'} fontWeight="400">
22+
Friends
23+
</Paragraph>
24+
<XStack flex={1} />
25+
<ChevronRight
26+
size={'$1.5'}
27+
color={'$lightGrayTextField'}
28+
$theme-light={{ color: '$darkGrayTextField' }}
29+
/>
30+
</Card.Header>
31+
<Card.Footer padded />
32+
</Card>
33+
)
34+
}

packages/app/features/home/InvestmentsBalanceCard.tsx

Lines changed: 114 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,35 @@
1-
import { Button, Card, Paragraph, Spinner, Theme, XStack, YStack } from '@my/ui'
1+
import {
2+
Card,
3+
type CardProps,
4+
H1,
5+
Paragraph,
6+
Spinner,
7+
Theme,
8+
useMedia,
9+
XStack,
10+
type XStackProps,
11+
} from '@my/ui'
212
import formatAmount from 'app/utils/formatAmount'
313

414
import { ChevronLeft, ChevronRight } from '@tamagui/lucide-icons'
515

616
import { useIsPriceHidden } from './utils/useIsPriceHidden'
717

818
import { useSendAccountBalances } from 'app/utils/useSendAccountBalances'
9-
import { investmentCoins } from 'app/data/coins'
19+
import { type CoinWithBalance, investmentCoins } from 'app/data/coins'
1020

1121
import { useRootScreenParams } from 'app/routers/params'
1222
import { useMultipleTokensMarketData } from 'app/utils/coin-gecko'
1323
import { useMemo } from 'react'
14-
import { IconError } from 'app/components/icons'
24+
import { IconCoin, IconError } from 'app/components/icons'
1525
import { useCoins } from 'app/provider/coins'
26+
import { investmentCoins as investmentCoinsList } from 'app/data/coins'
1627

17-
export const InvestmentsBalanceCard = () => {
28+
export const InvestmentsBalanceCard = (props: CardProps) => {
29+
const media = useMedia()
1830
const [queryParams, setParams] = useRootScreenParams()
1931
const isInvestmentCoin = investmentCoins.some(
20-
(coin) => coin.token.toLowerCase() === queryParams.token
32+
(coin) => coin.token.toLowerCase() === queryParams.token?.toLowerCase()
2133
)
2234
const isInvestmentsScreen = queryParams.token === 'investments'
2335

@@ -39,62 +51,107 @@ export const InvestmentsBalanceCard = () => {
3951
const formattedBalance = formatAmount(dollarTotal, 9, 0)
4052

4153
return (
42-
<Card py="$5" px="$4" w={'100%'} jc="space-between" onPress={toggleSubScreen}>
43-
<YStack jc={'center'} gap={'$5'} w={'100%'}>
44-
<YStack w={'100%'} gap={'$2.5'} jc="space-between">
45-
<XStack ai={'center'} jc={'space-between'} gap="$2.5" width={'100%'}>
46-
<Paragraph fontSize={'$7'} fontWeight="400">
47-
Invest
48-
</Paragraph>
49-
<Button
50-
chromeless
51-
backgroundColor="transparent"
52-
hoverStyle={{ backgroundColor: 'transparent' }}
53-
pressStyle={{
54-
backgroundColor: 'transparent',
55-
borderColor: 'transparent',
56-
}}
57-
focusStyle={{ backgroundColor: 'transparent' }}
58-
p={0}
59-
height={'auto'}
60-
>
61-
<Button.Icon>
62-
{isInvestmentCoin || isInvestmentsScreen ? (
63-
<ChevronLeft
64-
size={'$1.5'}
65-
color={'$lightGrayTextField'}
66-
$theme-light={{ color: '$darkGrayTextField' }}
67-
$lg={{ display: 'none' }}
68-
/>
69-
) : (
70-
<ChevronRight
71-
size={'$1.5'}
72-
color={'$primary'}
73-
$theme-light={{ color: '$color12' }}
74-
/>
75-
)}
76-
</Button.Icon>
77-
</Button>
78-
</XStack>
79-
</YStack>
80-
<Paragraph fontSize={'$10'} fontWeight={'600'} color={'$color12'}>
81-
{(() => {
82-
switch (true) {
83-
case isPriceHidden:
84-
return '///////'
85-
case isLoading || !dollarBalances:
86-
return <Spinner size={'large'} />
87-
default:
88-
return `$${formattedBalance}`
89-
}
90-
})()}
91-
</Paragraph>
92-
<InvestmentsAggregate />
93-
</YStack>
54+
<Card
55+
elevate
56+
hoverStyle={{ scale: 0.925 }}
57+
pressStyle={{ scale: 0.875 }}
58+
animation="bouncy"
59+
onPress={toggleSubScreen}
60+
size={'$5'}
61+
br="$7"
62+
w="100%"
63+
{...props}
64+
>
65+
<Card.Header padded>
66+
<XStack ai={'center'} jc={'space-between'} gap="$2.5" width={'100%'}>
67+
<Paragraph fontSize={'$7'} fontWeight="400">
68+
Invest
69+
</Paragraph>
70+
{isInvestmentCoin || isInvestmentsScreen ? (
71+
<ChevronLeft
72+
size={'$1.5'}
73+
color={'$primary'}
74+
$theme-light={{ color: '$color12' }}
75+
$lg={{ display: 'none' }}
76+
/>
77+
) : (
78+
<ChevronRight
79+
size={'$1.5'}
80+
color={'$lightGrayTextField'}
81+
$theme-light={{ color: '$darkGrayTextField' }}
82+
/>
83+
)}
84+
</XStack>
85+
</Card.Header>
86+
<Card.Footer padded fd="column" gap="$2">
87+
{isInvestmentsScreen && !media.gtLg ? (
88+
<>
89+
<H1 color={'$color12'}>
90+
{(() => {
91+
switch (true) {
92+
case isPriceHidden:
93+
return '///////'
94+
case isLoading || !dollarBalances:
95+
return <Spinner size={'large'} color={'$color12'} />
96+
default:
97+
return `$${formattedBalance}`
98+
}
99+
})()}
100+
</H1>
101+
<InvestmentsAggregate />
102+
</>
103+
) : (
104+
<InvestmentsPreview />
105+
)}
106+
</Card.Footer>
94107
</Card>
95108
)
96109
}
97110

111+
function InvestmentsPreview() {
112+
const { investmentCoins, isLoading } = useCoins()
113+
114+
if (isLoading) return <Spinner size="small" />
115+
116+
const existingSymbols = new Set(investmentCoins.map((coin) => coin.symbol))
117+
const coins = [
118+
...investmentCoins,
119+
...investmentCoinsList
120+
.filter((coin) => !existingSymbols.has(coin.symbol))
121+
.map((coin) => ({ ...coin, balance: 0n })),
122+
]
123+
124+
const sortedByBalance = coins.toSorted((a, b) =>
125+
(b?.balance ?? 0n) > (a?.balance ?? 0n) ? 1 : -1
126+
)
127+
return (
128+
<XStack ai="center" jc="space-between">
129+
<OverlappingCoinIcons coins={sortedByBalance} />
130+
<Card circular ai="center" jc="center" bc="$color0" w={'$3.5'} h="$3.5" mih={0} miw={0}>
131+
<Paragraph fontSize={'$4'} fontWeight="500">
132+
{`+${investmentCoinsList.length - 3}`}
133+
</Paragraph>
134+
</Card>
135+
</XStack>
136+
)
137+
}
138+
139+
function OverlappingCoinIcons({
140+
coins,
141+
length = 3,
142+
...props
143+
}: { coins: CoinWithBalance[]; length?: number } & XStackProps) {
144+
return (
145+
<XStack ai="center" {...props}>
146+
{coins.slice(0, length).map(({ symbol }) => (
147+
<Card key={symbol} circular mr={'$-5'} bc="transparent" ai="center" jc="center">
148+
<IconCoin size={'$3'} symbol={symbol} />
149+
</Card>
150+
))}
151+
</XStack>
152+
)
153+
}
154+
98155
function InvestmentsAggregate() {
99156
const tokenIds = useCoins()
100157
.investmentCoins.filter((c) => c?.balance && c.balance > 0n)
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { Card, type CardProps, H1, Paragraph, Spinner, XStack } from '@my/ui'
2+
import formatAmount from 'app/utils/formatAmount'
3+
import { ChevronRight } from '@tamagui/lucide-icons'
4+
import { useMemo } from 'react'
5+
import { useIsPriceHidden } from './utils/useIsPriceHidden'
6+
import { formatUnits } from 'viem'
7+
import { type LinkProps, useLink } from 'solito/link'
8+
import { useSupabase } from 'app/utils/supabase/useSupabase'
9+
import { useSendAccount } from 'app/utils/send-accounts'
10+
import { useQuery } from '@tanstack/react-query'
11+
import { useTokenPrices } from 'app/utils/useTokenPrices'
12+
import { baseMainnetClient, sendTokenAddress } from '@my/wagmi'
13+
import { coinsDict } from 'app/data/coins'
14+
15+
export const RewardsCard = ({ href, ...props }: Omit<CardProps & LinkProps, 'children'>) => {
16+
const linkProps = useLink({ href })
17+
const { isPriceHidden } = useIsPriceHidden()
18+
const { data: prices, isLoading: isPricesLoading } = useTokenPrices()
19+
const { data: distribution, isLoading: isDistributionLoading } = useCurrentDistributionShares()
20+
const shares = Number(
21+
formatUnits(
22+
BigInt(distribution?.distribution_shares?.[0]?.amount ?? 0n),
23+
coinsDict[sendTokenAddress[baseMainnetClient.chain.id]]?.formatDecimals ?? 18
24+
)
25+
)
26+
const sendPrice = prices?.[sendTokenAddress[baseMainnetClient.chain.id]] ?? 0
27+
28+
// Use the hook to get current asset values based on onchain rate
29+
const sharesValue = useMemo(() => formatAmount(shares * sendPrice, 0, 0), [shares, sendPrice])
30+
31+
const isLoading = isPricesLoading || isDistributionLoading
32+
33+
return (
34+
<Card
35+
elevate
36+
hoverStyle={{ scale: 0.925 }}
37+
pressStyle={{ scale: 0.875 }}
38+
animation="bouncy"
39+
size={'$5'}
40+
br="$7"
41+
{...linkProps}
42+
{...props}
43+
>
44+
<Card.Header padded fd="row" ai="center" jc="space-between">
45+
<Paragraph fontSize={'$7'} fontWeight="400">
46+
Rewards
47+
</Paragraph>
48+
<XStack flex={1} />
49+
<ChevronRight
50+
size={'$1.5'}
51+
color={'$lightGrayTextField'}
52+
$theme-light={{ color: '$darkGrayTextField' }}
53+
/>
54+
</Card.Header>
55+
<Card.Footer padded>
56+
<H1 color={'$color12'}>
57+
{(() => {
58+
switch (true) {
59+
case isPriceHidden:
60+
return '///////'
61+
case isLoading || !sharesValue:
62+
return <Spinner size={'large'} color={'$color12'} />
63+
default:
64+
return `$${sharesValue}`
65+
}
66+
})()}
67+
</H1>
68+
</Card.Footer>
69+
</Card>
70+
)
71+
}
72+
73+
const useCurrentDistributionShares = () => {
74+
const supabase = useSupabase()
75+
const { data: sendAccount } = useSendAccount()
76+
77+
return useQuery({
78+
queryKey: ['current_distribution_shares', supabase, sendAccount?.created_at],
79+
queryFn: async () => {
80+
const { data, error } = await supabase
81+
.from('distributions')
82+
.select(`
83+
number,
84+
distribution_shares(
85+
amount::text
86+
)
87+
`)
88+
.order('number', { ascending: false })
89+
.limit(1)
90+
.single()
91+
if (error) throw error
92+
return data
93+
},
94+
enabled: Boolean(sendAccount?.created_at),
95+
})
96+
}

0 commit comments

Comments
 (0)