Skip to content

Commit

Permalink
Add Initial Draft of "Get a Wallet" page (#194)
Browse files Browse the repository at this point in the history
  • Loading branch information
jribbink authored Jul 16, 2024
1 parent 83fc9e4 commit 8ca79ce
Show file tree
Hide file tree
Showing 13 changed files with 408 additions and 82 deletions.
51 changes: 16 additions & 35 deletions components/Discovery.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,27 @@
import { SUPPORTED_VERSIONS } from '../helpers/constants'
import { Container, Divider, HStack, Link, Stack, Text } from '@chakra-ui/react'
import { isGreaterThanOrEqualToVersion } from '../helpers/version'
import ServiceList from './ServiceList'
import { Flex, useModalContext } from '@chakra-ui/react'
import { useState } from 'react'
import { useWallets } from '../hooks/useWallets'
import { useConfig } from '../contexts/ConfigContext'
import GetWallet from './views/GetWallet'
import WalletSelection from './views/WalletSelection'

export default function Discovery() {
const { wallets, error } = useWallets()
const { appVersion } = useConfig()
const isFeaturesSupported = isGreaterThanOrEqualToVersion(
appVersion,
SUPPORTED_VERSIONS.SUGGESTED_FEATURES
)
const [learnMoreOpen, setLearnMoreOpen] = useState(false)
const modal = useModalContext()

if (!wallets) return <div />
if (error) return <div>Error Loading Data</div>

return (
<Stack overflow="hidden" spacing={0}>
<Container
display="flex"
flexDirection="column"
overflow="scroll"
px={8}
pb={4}
>
{/* TODO: this to be replaced with a filter bar & auto-suggest */}
{/* isFeaturesSupported && <Features /> */}
<ServiceList services={wallets} />
</Container>

<Divider borderColor="gray.300" />

<HStack justifyContent="space-between" alignItems="center" padding={6}>
<Text fontSize="sm">Don't have a wallet?</Text>

<Link href="https://fcl.dev" isExternal>
<Text fontSize="sm" color="blue.500">
Learn More
</Text>
</Link>
</HStack>
</Stack>
<Flex w={470} maxH={600} direction="column">
{learnMoreOpen ? (
<GetWallet
onBack={() => setLearnMoreOpen(false)}
onCloseModal={modal.onClose}
/>
) : (
<WalletSelection onSwitchToLearnMore={() => setLearnMoreOpen(true)} />
)}
</Flex>
)
}
76 changes: 76 additions & 0 deletions components/GetWalletCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { isExtension } from '../helpers/services'
import {
Button,
Card,
CardBody,
Flex,
HStack,
Image,
Stack,
Tag,
Text,
Link,
} from '@chakra-ui/react'
import { Service } from '../types'
import NextLink from 'next/link'

type Props = {
icon: string
name: string
service: Service
}

export default function GetWalletCard({ icon, name, service }: Props) {
const isExtensionService = isExtension(service)
const isExtensionServiceInstalled = Boolean(service?.provider?.is_installed)

return (
<Card size="md" variant="unstyled">
<CardBody width="100%" display="flex">
<Stack>
<Flex alignItems="center" justifyContent="space-between">
<HStack>
<Image
src={icon}
alt={name}
borderRadius="lg"
borderWidth="1px"
borderColor="gray.200"
borderStyle="solid"
boxSize="3.5rem"
alignSelf="start"
my="auto"
/>
<Flex direction="column" textAlign="left">
<Text fontSize="lg" as="b">
{name}
</Text>

{isExtensionService && !isExtensionServiceInstalled ? (
<Text fontSize="sm" color="gray.500">
Install Extension
</Text>
) : null}
</Flex>
</HStack>
</Flex>
</Stack>

{/* TODO: Needs to link to install page, will be addressed in future PR */}
<Button
href={service.provider.install_link || service.provider.website}
target="_blank"
as={NextLink}
variant="outline"
size="sm"
colorScheme="blue"
ml="auto"
alignSelf="center"
borderRadius="full"
>
Get
</Button>
</CardBody>
</Card>
)
}
22 changes: 22 additions & 0 deletions components/GetWalletList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Stack } from '@chakra-ui/react'
import { useWallets } from '../hooks/useWallets'
import GetWalletCard from './GetWalletCard'

export default function GetWalletList() {
const { wallets } = useWallets()

return (
<Stack spacing={4}>
{wallets.map((wallet, index) => {
return (
<GetWalletCard
key={wallet.provider.address ?? index}
service={wallet}
name={wallet?.provider?.name ?? ''}
icon={wallet?.provider?.icon ?? ''}
/>
)
})}
</Stack>
)
}
2 changes: 1 addition & 1 deletion components/ServiceCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export default function ServiceCard({ icon, name, service }: Props) {
<Image
src={icon}
alt={name}
borderRadius="md"
borderRadius="lg"
borderWidth="1px"
borderColor="gray.200"
borderStyle="solid"
Expand Down
3 changes: 3 additions & 0 deletions components/ServiceGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ interface ServiceGroupProps {
title: string
services: Service[]
titleProps?: React.ComponentProps<typeof Text>
cardProps?: React.ComponentProps<typeof ServiceCard>
}

export default function ServiceGroup({
title,
services,
titleProps,
cardProps,
}: ServiceGroupProps) {
return (
<Stack spacing={1}>
Expand All @@ -26,6 +28,7 @@ export default function ServiceGroup({
service={service}
name={service?.provider?.name ?? ''}
icon={service?.provider?.icon ?? ''}
{...cardProps}
/>
)
})}
Expand Down
2 changes: 1 addition & 1 deletion components/ServiceList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default function ServiceList({ services }: ServiceListProps) {
)

return (
<Stack spacing={2}>
<Stack spacing={4}>
{lastUsedService && (
<ServiceGroup
title="Last Used"
Expand Down
53 changes: 53 additions & 0 deletions components/ViewHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { CloseIcon } from '@chakra-ui/icons'
import { Flex, IconButton, Text } from '@chakra-ui/react'
import { IoChevronBack } from 'react-icons/io5'

type HeaderProps = {
title?: string
onClose?: () => void
onBack?: () => void
}

export default function ViewHeader({ title, onClose, onBack }: HeaderProps) {
return (
<Flex alignItems="center" position="relative" p="4">
<Text
position="absolute"
left="50%"
transform="translateX(-50%)"
fontSize="xl"
fontWeight="bold"
my="auto"
>
{title}
</Text>

{onBack && (
<IconButton
onClick={onBack}
icon={<IoChevronBack />}
aria-label="Close Modal"
borderRadius="full"
variant="ghost"
size="sm"
color="blue.500"
fontWeight="bold"
fontSize="1.5rem"
></IconButton>
)}

{onClose && (
<IconButton
onClick={onClose}
icon={<CloseIcon />}
aria-label="Close Modal"
borderRadius="full"
bg="gray.100"
color="black"
ml="auto"
size="sm"
></IconButton>
)}
</Flex>
)
}
17 changes: 17 additions & 0 deletions components/ViewLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Stack } from '@chakra-ui/react'
import ViewHeader from './ViewHeader'
import { ComponentProps, ReactNode } from 'react'

type ViewLayoutProps = {
children: ReactNode
header?: ComponentProps<typeof ViewHeader>
}

export default function ViewLayout({ children, header }: ViewLayoutProps) {
return (
<Stack spacing={0} overflow="hidden">
{header && <ViewHeader {...header} />}
{children}
</Stack>
)
}
31 changes: 31 additions & 0 deletions components/views/GetWallet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Container, Stack, Text, VStack } from '@chakra-ui/react'
import ViewLayout from '../ViewLayout'
import GetWalletList from '../GetWalletList'

interface GetWalletProps {
onBack?: () => void
onCloseModal?: () => void
}

export default function GetWallet({ onBack, onCloseModal }: GetWalletProps) {
return (
<ViewLayout
header={{ title: 'Get a Wallet', onBack, onClose: onCloseModal }}
>
<Stack spacing={0} flexGrow={1} overflow="hidden">
<Stack spacing={8} px={8} pb={6} flexGrow={1} overflow="scroll">
<GetWalletList />
</Stack>
<Container textAlign="center" p={8} maxW="xs">
<Text fontSize="md" fontWeight="bold" mb={1}>
Not what you're looking for?
</Text>
<Text fontSize="sm" color="gray.500">
Select a wallet on the left to get started with a different wallet
provider.
</Text>
</Container>
</Stack>
</ViewLayout>
)
}
57 changes: 57 additions & 0 deletions components/views/WalletSelection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import {
Button,
Divider,
Flex,
HStack,
Stack,
Text,
useModalContext,
} from '@chakra-ui/react'
import ServiceList from '../ServiceList'
import ViewLayout from '../ViewLayout'
import { useWallets } from '../../hooks/useWallets'
import { isGreaterThanOrEqualToVersion } from '../../helpers/version'
import { useConfig } from '../../contexts/ConfigContext'
import { SUPPORTED_VERSIONS } from '../../helpers/constants'

type Props = {
onSwitchToLearnMore: () => void
}

export default function WalletSelection({ onSwitchToLearnMore }: Props) {
const modal = useModalContext()
const { wallets } = useWallets()
const { appVersion } = useConfig()
const isFeaturesSupported = isGreaterThanOrEqualToVersion(
appVersion,
SUPPORTED_VERSIONS.SUGGESTED_FEATURES
)

return (
<ViewLayout
header={{ title: 'Select a Wallet', onClose: () => modal.onClose() }}
>
<Stack spacing={0} flexGrow={1} overflow="hidden">
<Stack overflow="scroll" px={8} pb={6} flexGrow={1}>
{/* TODO: replace this in future PR with Filter Bar */}
{/*isFeaturesSupported && <Features />*/}
<ServiceList services={wallets} />
</Stack>

<Divider color="gray.300" />

<HStack justifyContent="space-between" alignItems="center" padding={8}>
<Text fontSize="sm" color="gray.500">
Don't have a wallet?
</Text>

<Button onClick={onSwitchToLearnMore} variant="link" padding={0}>
<Text fontSize="sm" color="blue.500">
Learn More
</Text>
</Button>
</HStack>
</Stack>
</ViewLayout>
)
}
7 changes: 4 additions & 3 deletions hooks/useWallets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@ import useSWR from 'swr'
import { PATHS } from '../helpers/constants'
import { useConfig } from '../contexts/ConfigContext'
import { getUserAgent } from '../helpers/userAgent'
import { Service } from '../types'

const genKey = (url: string, opts: any) => [url, JSON.stringify(opts)]

const fetcher = async (url: string, opts: any) => {
const fetcher = async <T>(url: string, opts: any) => {
return fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(opts),
}).then(d => d.json())
}).then(d => d.json() as Promise<T>)
}

export function useWallets() {
Expand Down Expand Up @@ -42,7 +43,7 @@ export function useWallets() {
}

const { data: wallets, error } = useSWR(genKey(requestUrl, body), url =>
fetcher(url, body)
fetcher<Service[]>(url, body)
)

return { wallets, error, isLoading: !wallets && !error }
Expand Down
Loading

0 comments on commit 8ca79ce

Please sign in to comment.