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

Add Connect Wallet page #207

Merged
merged 8 commits into from
Jul 30, 2024
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
33 changes: 27 additions & 6 deletions components/Discovery.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import { Flex, useModalContext } from '@chakra-ui/react'
import { useState } from 'react'
import { useWallets } from '../hooks/useWallets'
import { Wallet } from '../data/wallets'

import WalletSelection from './views/WalletSelection'
import ExploreWallets from './views/ExploreWallets'
import GetWallet from './views/GetWallet'
import ScanInstall from './views/ScanInstall'
import ConnectWallet from './views/ConnectWallet'
import { Flex, useModalContext } from '@chakra-ui/react'
import { useState } from 'react'
import { useWallets } from '../hooks/useWallets'
import { Wallet } from '../data/wallets'
import * as fcl from '@onflow/fcl'

export enum VIEWS {
WALLET_SELECTION,
EXPLORE_WALLETS,
GET_WALLET,
SCAN_INSTALL,
CONNECT_WALLET,
SCAN_CONNECT,
}

Expand All @@ -31,6 +33,16 @@ export default function Discovery() {
viewContent = (
<WalletSelection
onSwitchToLearnMore={() => setCurrentView(VIEWS.EXPLORE_WALLETS)}
onClickWallet={wallet => {
setSelectedWallet(wallet)
if (wallet.services.length === 1) {
// TODO: make sure WC/RPC behaviour is handled once integrated into Discovery
// (future PR)
fcl.WalletUtils.redirect(wallet.services[0])
} else {
setCurrentView(VIEWS.CONNECT_WALLET)
}
}}
/>
)
break
Expand Down Expand Up @@ -64,12 +76,21 @@ export default function Discovery() {
<ScanInstall
onBack={() => setCurrentView(VIEWS.WALLET_SELECTION)}
onCloseModal={modal.onClose}
/* TODO: This should link to the CONNECT_WALLET view once added */
// TODO: Implement next page
onContinue={() => setCurrentView(VIEWS.WALLET_SELECTION)}
wallet={selectedWallet}
/>
)
break
case VIEWS.CONNECT_WALLET:
viewContent = (
<ConnectWallet
onBack={() => setCurrentView(VIEWS.WALLET_SELECTION)}
onCloseModal={modal.onClose}
wallet={selectedWallet}
/>
)
break
}

return (
Expand Down
7 changes: 3 additions & 4 deletions components/GetWalletCard.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { isExtension } from '../helpers/services'
import {
Button,
Card,
CardBody,
Flex,
Expand All @@ -14,10 +13,10 @@ import HybridButton from './HybridButton'

type Props = {
wallet: Wallet
onButtonClick: () => void
onGetWallet?: () => void
}

export default function GetWalletCard({ wallet, onButtonClick }: Props) {
export default function GetWalletCard({ wallet, onGetWallet }: Props) {
const extensionService = wallet.services.find(isExtension)
const isExtensionService = !!extensionService
const isExtensionServiceInstalled = extensionService?.provider?.is_installed
Expand Down Expand Up @@ -64,7 +63,7 @@ export default function GetWalletCard({ wallet, onButtonClick }: Props) {
{...(!wallet.installLink
? { href: wallet.website }
: {
onClick: onButtonClick,
onClick: onGetWallet,
})}
>
Get
Expand Down
2 changes: 1 addition & 1 deletion components/GetWalletList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default function GetWalletList({ onGetWallet }: GetWalletListProps) {
<GetWalletCard
key={wallet.uid}
wallet={wallet}
onButtonClick={() => onGetWallet(wallet)}
onGetWallet={() => onGetWallet(wallet)}
/>
)
})}
Expand Down
10 changes: 3 additions & 7 deletions components/ServiceCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,20 @@ import {
HStack,
Image,
Stack,
Tag,
Text,
} from '@chakra-ui/react'
import { Wallet } from '../data/wallets'

interface ServiceCardProps {
wallet: Wallet
onClick: () => void
}

export default function ServiceCard({ wallet }: ServiceCardProps) {
export default function ServiceCard({ wallet, onClick }: ServiceCardProps) {
const extensionService = wallet.services.find(isExtension)
const isExtensionService = !!extensionService
const isExtensionServiceInstalled = extensionService?.provider?.is_installed

const onSelect = () => {
// TODO: implement connect wallet logic, future PR
}

return (
<Card
size="sm"
Expand All @@ -33,7 +29,7 @@ export default function ServiceCard({ wallet }: ServiceCardProps) {
transitionDuration: '0.2s',
transitionTimingFunction: 'ease-in-out',
}}
onClick={onSelect}
onClick={onClick}
variant="unstyled"
>
<CardBody width="100%">
Expand Down
11 changes: 10 additions & 1 deletion components/ServiceGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ interface ServiceGroupProps {
wallets: Wallet[]
titleProps?: React.ComponentProps<typeof Text>
cardProps?: React.ComponentProps<typeof ServiceCard>
onClickWallet: (wallet: Wallet) => void
}

export default function ServiceGroup({
title,
wallets,
titleProps,
cardProps,
onClickWallet,
}: ServiceGroupProps) {
return (
<Stack spacing={1}>
Expand All @@ -22,7 +24,14 @@ export default function ServiceGroup({
</Text>
<Stack spacing={2}>
{wallets.map(wallet => {
return <ServiceCard key={wallet.uid} wallet={wallet} {...cardProps} />
return (
<ServiceCard
key={wallet.uid}
wallet={wallet}
onClick={() => onClickWallet(wallet)}
{...cardProps}
/>
)
})}
</Stack>
</Stack>
Expand Down
19 changes: 16 additions & 3 deletions components/ServiceList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@ import { useWalletHistory } from '../hooks/useWalletHistory'

interface ServiceListProps {
wallets: Wallet[]
onClickWallet: (wallet: Wallet) => void
}

export default function ServiceList({ wallets }: ServiceListProps) {
export default function ServiceList({
wallets,
onClickWallet,
}: ServiceListProps) {
const { isLastUsed } = useWalletHistory()

// Get the last used service, installed services, and recommended services
Expand Down Expand Up @@ -46,15 +50,24 @@ export default function ServiceList({ wallets }: ServiceListProps) {
titleProps={{
color: 'blue.400',
}}
onClickWallet={onClickWallet}
/>
)}

{installedWallets.length > 0 && (
<ServiceGroup title="Installed" wallets={installedWallets} />
<ServiceGroup
title="Installed"
wallets={installedWallets}
onClickWallet={onClickWallet}
/>
)}

{recommendedWallets.length > 0 && (
<ServiceGroup title="Recommended" wallets={recommendedWallets} />
<ServiceGroup
title="Recommended"
wallets={recommendedWallets}
onClickWallet={onClickWallet}
/>
)}
</Stack>
)
Expand Down
10 changes: 8 additions & 2 deletions components/WalletTypeCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ type WalletTypeCardProps = {
icon: string
title: string
description: string
unstyled?: boolean
button: {
text: string
} & (
Expand All @@ -23,17 +24,22 @@ export default function WalletTypeCard({
icon,
title,
description,
unstyled,
button: { text: buttonText, ...buttonProps },
}: WalletTypeCardProps) {
return (
<Stack
borderWidth="1px"
borderRadius="2xl"
flex={1}
width="100%"
justifyContent="center"
alignItems="center"
backgroundColor="gray.100"
{...(!unstyled
? {
borderWidth: '1px',
backgroundColor: 'gray.100',
}
: {})}
>
<HStack p={4} w="sm" spacing={8}>
<Image
Expand Down
89 changes: 89 additions & 0 deletions components/views/ConnectWallet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { Divider, Stack } from '@chakra-ui/react'
import ViewLayout from '../ViewLayout'
import WalletTypeCard from '../WalletTypeCard'
import ChromeIcon from '../Icons/chrome.svg'
import { Wallet } from '../../data/wallets'
import { FCL_SERVICE_METHODS } from '../../helpers/constants'
import * as fcl from '@onflow/fcl'
import { Fragment } from 'react'
import { Service } from '../../types'
import { toTitleCase } from '../../helpers/strings'

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

export default function ConnectWallet({
onBack,
onCloseModal,
wallet,
}: GetWalletProps) {
const getServiceInfo = (service: Service) => {
const titleCasedName = toTitleCase(wallet.name)
let title: string, description: string, buttonText: string, icon: string
switch (service.method) {
case FCL_SERVICE_METHODS.WC:
title = `${titleCasedName} Mobile`
description = `Confirm the connection in the mobile app`
buttonText = `Scan QR Code`
icon = wallet.icon
break
case FCL_SERVICE_METHODS.EXT:
title = `${titleCasedName} Extension`
description = `Confirm the connection in the browser extension`
buttonText = `Connect`
icon = ChromeIcon
break
case FCL_SERVICE_METHODS.HTTP:
case FCL_SERVICE_METHODS.POP:
case FCL_SERVICE_METHODS.IFRAME:
case FCL_SERVICE_METHODS.TAB:
title = `Connect to ${titleCasedName}`
description = `Confirm the connection in the web app`
buttonText = `Connect`
icon = ChromeIcon
break
default:
title = `Connect to ${titleCasedName}`
description = `Confirm the connection in your wallet`
buttonText = `Connect`
}
return { title, description, buttonText, icon }
}

return (
<ViewLayout
header={{
title: `Connect to ${wallet.name}`,
onBack,
onClose: onCloseModal,
}}
>
<Stack flexGrow={1} alignItems="center" spacing={4} px={6} pb={6}>
{wallet.services.map((service, i) => {
const { title, description, buttonText, icon } =
getServiceInfo(service)
return (
<Fragment key={i}>
<WalletTypeCard
icon={icon}
title={title}
description={description}
button={{
text: buttonText,
onClick: () => fcl.WalletUtils.redirect(service),
}}
unstyled
></WalletTypeCard>
{i < wallet.services.length - 1 && (
<Divider w="90%" borderColor="gray.300" />
)}
</Fragment>
)
})}
</Stack>
</ViewLayout>
)
}
9 changes: 7 additions & 2 deletions components/views/WalletSelection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,17 @@ import { useWallets } from '../../hooks/useWallets'
import { isGreaterThanOrEqualToVersion } from '../../helpers/version'
import { useConfig } from '../../contexts/ConfigContext'
import { SUPPORTED_VERSIONS } from '../../helpers/constants'
import { Wallet } from '../../data/wallets'

type Props = {
onClickWallet: (wallet: Wallet) => void
onSwitchToLearnMore: () => void
}

export default function WalletSelection({ onSwitchToLearnMore }: Props) {
export default function WalletSelection({
onSwitchToLearnMore,
onClickWallet,
}: Props) {
const modal = useModalContext()
const { wallets } = useWallets()
const { appVersion } = useConfig()
Expand All @@ -34,7 +39,7 @@ export default function WalletSelection({ onSwitchToLearnMore }: Props) {
<Stack overflow="scroll" px={8} pb={6} flexGrow={1}>
{/* TODO: replace this in future PR with Filter Bar */}
{/*isFeaturesSupported && <Features />*/}
<ServiceList wallets={wallets} />
<ServiceList onClickWallet={onClickWallet} wallets={wallets} />
</Stack>

<Divider color="gray.300" />
Expand Down
27 changes: 27 additions & 0 deletions helpers/__tests__/strings.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { toTitleCase } from '../strings'

describe('strings helpers tests', () => {
test('to title case', () => {
const str = 'hello world'
const result = toTitleCase(str)
expect(result).toBe('Hello World')
})

test('to title case with empty string', () => {
const str = ''
const result = toTitleCase(str)
expect(result).toBe('')
})

test('to title case with null', () => {
const str = null
const result = toTitleCase(str)
expect(result).toBe(null)
})

test('to title case with undefined', () => {
const str = undefined
const result = toTitleCase(str)
expect(result).toBe(undefined)
})
})
Loading
Loading