From afaea8c1dc1c50df281202e6c0116e8ffe183206 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Mon, 29 Jul 2024 16:25:53 -0700 Subject: [PATCH 1/7] Add Connect Wallet page --- components/Discovery.tsx | 33 +++++++++++--- components/GetWalletCard.tsx | 7 ++- components/GetWalletList.tsx | 2 +- components/ServiceCard.tsx | 10 ++--- components/ServiceGroup.tsx | 11 ++++- components/ServiceList.tsx | 19 +++++++-- components/WalletTypeCard.tsx | 9 +++- components/views/ConnectWallet.tsx | 64 ++++++++++++++++++++++++++++ components/views/WalletSelection.tsx | 9 +++- contexts/NavigationContext.tsx | 41 ++++++++++++++++++ helpers/strings.js | 7 --- helpers/strings.ts | 15 +++++++ hooks/useWallets.ts | 1 + 13 files changed, 196 insertions(+), 32 deletions(-) create mode 100644 components/views/ConnectWallet.tsx create mode 100644 contexts/NavigationContext.tsx delete mode 100644 helpers/strings.js create mode 100644 helpers/strings.ts diff --git a/components/Discovery.tsx b/components/Discovery.tsx index e88b9d04..e84ab0cd 100644 --- a/components/Discovery.tsx +++ b/components/Discovery.tsx @@ -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, } @@ -31,6 +33,16 @@ export default function Discovery() { viewContent = ( 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 @@ -64,12 +76,21 @@ export default function Discovery() { 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 = ( + setCurrentView(VIEWS.WALLET_SELECTION)} + onCloseModal={modal.onClose} + wallet={selectedWallet} + /> + ) + break } return ( diff --git a/components/GetWalletCard.tsx b/components/GetWalletCard.tsx index 51c47fa6..71c46f63 100644 --- a/components/GetWalletCard.tsx +++ b/components/GetWalletCard.tsx @@ -1,6 +1,5 @@ import { isExtension } from '../helpers/services' import { - Button, Card, CardBody, Flex, @@ -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 @@ -64,7 +63,7 @@ export default function GetWalletCard({ wallet, onButtonClick }: Props) { {...(!wallet.installLink ? { href: wallet.website } : { - onClick: onButtonClick, + onClick: onGetWallet, })} > Get diff --git a/components/GetWalletList.tsx b/components/GetWalletList.tsx index 740addb3..cad250aa 100644 --- a/components/GetWalletList.tsx +++ b/components/GetWalletList.tsx @@ -17,7 +17,7 @@ export default function GetWalletList({ onGetWallet }: GetWalletListProps) { onGetWallet(wallet)} + onGetWallet={() => onGetWallet(wallet)} /> ) })} diff --git a/components/ServiceCard.tsx b/components/ServiceCard.tsx index 8e6b2d3f..d29925c9 100644 --- a/components/ServiceCard.tsx +++ b/components/ServiceCard.tsx @@ -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 ( diff --git a/components/ServiceGroup.tsx b/components/ServiceGroup.tsx index 7605d222..c18b3133 100644 --- a/components/ServiceGroup.tsx +++ b/components/ServiceGroup.tsx @@ -7,6 +7,7 @@ interface ServiceGroupProps { wallets: Wallet[] titleProps?: React.ComponentProps cardProps?: React.ComponentProps + onClickWallet: (wallet: Wallet) => void } export default function ServiceGroup({ @@ -14,6 +15,7 @@ export default function ServiceGroup({ wallets, titleProps, cardProps, + onClickWallet, }: ServiceGroupProps) { return ( @@ -22,7 +24,14 @@ export default function ServiceGroup({ {wallets.map(wallet => { - return + return ( + onClickWallet(wallet)} + {...cardProps} + /> + ) })} diff --git a/components/ServiceList.tsx b/components/ServiceList.tsx index bc713aa5..38ca98ee 100644 --- a/components/ServiceList.tsx +++ b/components/ServiceList.tsx @@ -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 @@ -46,15 +50,24 @@ export default function ServiceList({ wallets }: ServiceListProps) { titleProps={{ color: 'blue.400', }} + onClickWallet={onClickWallet} /> )} {installedWallets.length > 0 && ( - + )} {recommendedWallets.length > 0 && ( - + )} ) diff --git a/components/WalletTypeCard.tsx b/components/WalletTypeCard.tsx index ef6a088a..4e9dbd79 100644 --- a/components/WalletTypeCard.tsx +++ b/components/WalletTypeCard.tsx @@ -7,6 +7,7 @@ type WalletTypeCardProps = { icon: string title: string description: string + unstyled?: boolean button: { text: string } & ( @@ -23,6 +24,7 @@ export default function WalletTypeCard({ icon, title, description, + unstyled, button: { text: buttonText, ...buttonProps }, }: WalletTypeCardProps) { return ( @@ -33,7 +35,12 @@ export default function WalletTypeCard({ width="100%" justifyContent="center" alignItems="center" - backgroundColor="gray.100" + {...(!unstyled + ? { + borderWidth: '1px', + backgroundColor: 'gray.100', + } + : {})} > void + onCloseModal: () => void + wallet: Wallet +} + +export default function ConnectWallet({ + onBack, + onCloseModal, + wallet, +}: GetWalletProps) { + return ( + + + {wallet.services.map((service, i) => { + const cannonicalName = CANNONICAL_SERVICE_NAMES[service.method] + return ( + + fcl.WalletUtils.redirect(service), + }} + unstyled + > + {i < wallet.services.length - 1 && } + + ) + })} + + + ) +} diff --git a/components/views/WalletSelection.tsx b/components/views/WalletSelection.tsx index c96e8188..f23ac5d2 100644 --- a/components/views/WalletSelection.tsx +++ b/components/views/WalletSelection.tsx @@ -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() @@ -34,7 +39,7 @@ export default function WalletSelection({ onSwitchToLearnMore }: Props) { {/* TODO: replace this in future PR with Filter Bar */} {/*isFeaturesSupported && */} - + diff --git a/contexts/NavigationContext.tsx b/contexts/NavigationContext.tsx new file mode 100644 index 00000000..9653cc7d --- /dev/null +++ b/contexts/NavigationContext.tsx @@ -0,0 +1,41 @@ +import { createContext, useContext, useState } from 'react' + +export const NavigationContext = createContext({ + navigate: (route: string) => { + console.log('Navigate to', route) + }, + goBack: () => { + console.log('Go back') + }, + history: [], + currentRoute: '', +}) + +export function Navigator({ children }) { + const [history, setHistory] = useState([]) + const [currentRoute, setCurrentRoute] = useState('') + + const navigate = route => { + setHistory([...history, currentRoute]) + setCurrentRoute(route) + } + + const goBack = () => { + const previousRoute = history.pop() + setCurrentRoute(previousRoute) + } + + return ( + + ) +} + +export function Route({ component }) { + return component +} + +export function useNavigation() { + return useContext(NavigationContext) +} diff --git a/helpers/strings.js b/helpers/strings.js deleted file mode 100644 index 2ae2b3ab..00000000 --- a/helpers/strings.js +++ /dev/null @@ -1,7 +0,0 @@ -export const truncateString = (str, n) => { - if (str.length > n) { - return str.substring(0, n) + '...' - } else { - return str - } -} diff --git a/helpers/strings.ts b/helpers/strings.ts new file mode 100644 index 00000000..dc6549f8 --- /dev/null +++ b/helpers/strings.ts @@ -0,0 +1,15 @@ +export const truncateString = (str: string, n: number) => { + if (str.length > n) { + return str.substring(0, n) + '...' + } else { + return str + } +} + +export const toTitleCase = (str: string) => + str + ?.split(' ') + ?.map(c => + c.length > 0 ? c[0].toUpperCase() + c.substring(1).toLowerCase() : c + ) + ?.join(' ') diff --git a/hooks/useWallets.ts b/hooks/useWallets.ts index cfc8759f..942a5128 100644 --- a/hooks/useWallets.ts +++ b/hooks/useWallets.ts @@ -1,4 +1,5 @@ import useSWR from 'swr' +import { PATHS } from '../helpers/constants' import { useConfig } from '../contexts/ConfigContext' import { getUserAgent } from '../helpers/platform' import { Wallet } from '../data/wallets' From 05a28f7af9bb333e023cdc7183b6fab944589b98 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Mon, 29 Jul 2024 16:39:37 -0700 Subject: [PATCH 2/7] styling --- components/WalletTypeCard.tsx | 1 - components/views/ConnectWallet.tsx | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/components/WalletTypeCard.tsx b/components/WalletTypeCard.tsx index 4e9dbd79..43d3e7fd 100644 --- a/components/WalletTypeCard.tsx +++ b/components/WalletTypeCard.tsx @@ -29,7 +29,6 @@ export default function WalletTypeCard({ }: WalletTypeCardProps) { return ( - {i < wallet.services.length - 1 && } + {i < wallet.services.length - 1 && ( + + )} ) })} From 190cd54e75844a96e98bffb0c05ed5a3bc500f6c Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Tue, 30 Jul 2024 09:23:20 -0700 Subject: [PATCH 3/7] Update text --- components/views/ConnectWallet.tsx | 37 +++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/components/views/ConnectWallet.tsx b/components/views/ConnectWallet.tsx index 76668190..e000d1da 100644 --- a/components/views/ConnectWallet.tsx +++ b/components/views/ConnectWallet.tsx @@ -29,6 +29,33 @@ export default function ConnectWallet({ onCloseModal, wallet, }: GetWalletProps) { + const getServiceInfo = service => { + let title: string, description: string, buttonText: string + switch (wallet.services[0].method) { + case 'WC/RPC': + title = `${wallet.name} Mobile` + description = `Confirm the connection in the mobile app` + buttonText = `Scan with Phone` + break + case FCL_SERVICE_METHODS.EXT: + title = `${wallet.name} Extension` + description = `Confirm the connection in the browser extension` + buttonText = `Connect` + break + case FCL_SERVICE_METHODS.HTTP: + case FCL_SERVICE_METHODS.POP: + case FCL_SERVICE_METHODS.IFRAME: + case FCL_SERVICE_METHODS.TAB: + title = `Connect to ${wallet.name} Web Browser` + description = `Confirm the connection in the ${toLower( + CANNONICAL_SERVICE_NAMES[wallet.services[0].method] + )}` + buttonText = `Connect` + break + } + return { title, description, buttonText } + } + return ( {wallet.services.map((service, i) => { - const cannonicalName = CANNONICAL_SERVICE_NAMES[service.method] + const { title, description, buttonText } = getServiceInfo(service) return ( fcl.WalletUtils.redirect(service), }} unstyled From 9fdd92e5289117bbf03f9873bc1da5c23e612138 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Tue, 30 Jul 2024 09:24:14 -0700 Subject: [PATCH 4/7] update connect text --- components/views/ConnectWallet.tsx | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/components/views/ConnectWallet.tsx b/components/views/ConnectWallet.tsx index e000d1da..3f0bb7f1 100644 --- a/components/views/ConnectWallet.tsx +++ b/components/views/ConnectWallet.tsx @@ -4,20 +4,10 @@ import WalletTypeCard from '../WalletTypeCard' import ChromeIcon from '../Icons/chrome.svg' import { Wallet } from '../../data/wallets' import { FCL_SERVICE_METHODS } from '../../helpers/constants' -import { toTitleCase } from '../../helpers/strings' import { toLower } from 'rambda' import * as fcl from '@onflow/fcl' import { Fragment } from 'react' -const CANNONICAL_SERVICE_NAMES = { - 'WC/RPC': 'Mobile App', - [FCL_SERVICE_METHODS.EXT]: 'Browser Extension', - [FCL_SERVICE_METHODS.HTTP]: 'Web Browser', - [FCL_SERVICE_METHODS.TAB]: 'Web Browser', - [FCL_SERVICE_METHODS.POP]: 'Web Browser', - [FCL_SERVICE_METHODS.IFRAME]: 'Web Browser', -} - interface GetWalletProps { onBack: () => void onCloseModal: () => void @@ -46,12 +36,14 @@ export default function ConnectWallet({ case FCL_SERVICE_METHODS.POP: case FCL_SERVICE_METHODS.IFRAME: case FCL_SERVICE_METHODS.TAB: - title = `Connect to ${wallet.name} Web Browser` - description = `Confirm the connection in the ${toLower( - CANNONICAL_SERVICE_NAMES[wallet.services[0].method] - )}` + title = `Connect to ${wallet.name}` + description = `Confirm the connection in the web app` buttonText = `Connect` break + default: + title = `Connect to ${wallet.name}` + description = `Confirm the connection in your wallet` + buttonText = `Connect` } return { title, description, buttonText } } From a77552ff4f286a549f5c0a58aeedd2c9dad07a26 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Tue, 30 Jul 2024 10:26:43 -0700 Subject: [PATCH 5/7] Update appearance --- components/views/ConnectWallet.tsx | 20 ++++++++++++-------- helpers/strings.ts | 8 -------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/components/views/ConnectWallet.tsx b/components/views/ConnectWallet.tsx index 3f0bb7f1..06e0a752 100644 --- a/components/views/ConnectWallet.tsx +++ b/components/views/ConnectWallet.tsx @@ -4,9 +4,9 @@ import WalletTypeCard from '../WalletTypeCard' import ChromeIcon from '../Icons/chrome.svg' import { Wallet } from '../../data/wallets' import { FCL_SERVICE_METHODS } from '../../helpers/constants' -import { toLower } from 'rambda' import * as fcl from '@onflow/fcl' import { Fragment } from 'react' +import { Service } from '../../types' interface GetWalletProps { onBack: () => void @@ -19,18 +19,20 @@ export default function ConnectWallet({ onCloseModal, wallet, }: GetWalletProps) { - const getServiceInfo = service => { - let title: string, description: string, buttonText: string - switch (wallet.services[0].method) { + const getServiceInfo = (service: Service) => { + let title: string, description: string, buttonText: string, icon: string + switch (service.method) { case 'WC/RPC': title = `${wallet.name} Mobile` description = `Confirm the connection in the mobile app` buttonText = `Scan with Phone` + icon = wallet.icon break case FCL_SERVICE_METHODS.EXT: title = `${wallet.name} Extension` description = `Confirm the connection in the browser extension` buttonText = `Connect` + icon = ChromeIcon break case FCL_SERVICE_METHODS.HTTP: case FCL_SERVICE_METHODS.POP: @@ -39,13 +41,14 @@ export default function ConnectWallet({ title = `Connect to ${wallet.name}` description = `Confirm the connection in the web app` buttonText = `Connect` + icon = ChromeIcon break default: title = `Connect to ${wallet.name}` description = `Confirm the connection in your wallet` buttonText = `Connect` } - return { title, description, buttonText } + return { title, description, buttonText, icon } } return ( @@ -58,11 +61,12 @@ export default function ConnectWallet({ > {wallet.services.map((service, i) => { - const { title, description, buttonText } = getServiceInfo(service) + const { title, description, buttonText, icon } = + getServiceInfo(service) return ( {i < wallet.services.length - 1 && ( - + )} ) diff --git a/helpers/strings.ts b/helpers/strings.ts index dc6549f8..4d454336 100644 --- a/helpers/strings.ts +++ b/helpers/strings.ts @@ -5,11 +5,3 @@ export const truncateString = (str: string, n: number) => { return str } } - -export const toTitleCase = (str: string) => - str - ?.split(' ') - ?.map(c => - c.length > 0 ? c[0].toUpperCase() + c.substring(1).toLowerCase() : c - ) - ?.join(' ') From 4ac242a700a30d6bf3d7e5ab32a1b1ee9826ecc5 Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Tue, 30 Jul 2024 10:28:32 -0700 Subject: [PATCH 6/7] remove unused import --- hooks/useWallets.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/hooks/useWallets.ts b/hooks/useWallets.ts index 942a5128..cfc8759f 100644 --- a/hooks/useWallets.ts +++ b/hooks/useWallets.ts @@ -1,5 +1,4 @@ import useSWR from 'swr' -import { PATHS } from '../helpers/constants' import { useConfig } from '../contexts/ConfigContext' import { getUserAgent } from '../helpers/platform' import { Wallet } from '../data/wallets' From db0cf3830e361f1b36d6c0c2d0d51161ace626fe Mon Sep 17 00:00:00 2001 From: Jordan Ribbink Date: Tue, 30 Jul 2024 10:29:06 -0700 Subject: [PATCH 7/7] remove nav context --- contexts/NavigationContext.tsx | 41 ---------------------------------- 1 file changed, 41 deletions(-) delete mode 100644 contexts/NavigationContext.tsx diff --git a/contexts/NavigationContext.tsx b/contexts/NavigationContext.tsx deleted file mode 100644 index 9653cc7d..00000000 --- a/contexts/NavigationContext.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { createContext, useContext, useState } from 'react' - -export const NavigationContext = createContext({ - navigate: (route: string) => { - console.log('Navigate to', route) - }, - goBack: () => { - console.log('Go back') - }, - history: [], - currentRoute: '', -}) - -export function Navigator({ children }) { - const [history, setHistory] = useState([]) - const [currentRoute, setCurrentRoute] = useState('') - - const navigate = route => { - setHistory([...history, currentRoute]) - setCurrentRoute(route) - } - - const goBack = () => { - const previousRoute = history.pop() - setCurrentRoute(previousRoute) - } - - return ( - - ) -} - -export function Route({ component }) { - return component -} - -export function useNavigation() { - return useContext(NavigationContext) -}