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

Supported suggested features by apps #161

Merged
merged 11 commits into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from 10 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
17 changes: 15 additions & 2 deletions components/Discovery.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import useSWR from 'swr'
import { sortByAddress } from '../helpers/services'
import { LOCAL_STORAGE_KEYS, PATHS } from '../helpers/constants'
import { LOCAL_STORAGE_KEYS, PATHS, SUPPORTED_VERSIONS } from '../helpers/constants'
import ServiceCard from './ServiceCard'
import Header from './Headers/Header'
import { useLocalStorage } from '../hooks/useLocalStorage'
import { getUserAgent } from '../helpers/userAgent'
import { Container, Stack } from '@chakra-ui/react'
import { Container, Text, Stack } from '@chakra-ui/react'
import { Service, Strategy } from '../types'
import Features from './Features'
import { isGreaterThanOrEqualToVersion } from '../helpers/version'

const fetcher = (url, opts) => {
return fetch(url, {
Expand All @@ -25,6 +27,7 @@ type Props = {
walletInclude: string[]
clientServices: Service[]
supportedStrategies: Strategy[]
clientConfig: { [key: string]: any }
port: number
}

Expand All @@ -35,6 +38,7 @@ export const Discovery = ({
walletInclude,
clientServices,
supportedStrategies,
clientConfig,
port,
}: Props) => {
const requestUrl = `/api${PATHS[network.toUpperCase()]}?discoveryType=UI`
Expand All @@ -43,6 +47,9 @@ export const Discovery = ({
type: ['authn'],
fclVersion: appVersion,
include: walletInclude,
features: {
suggested: clientConfig?.discoveryFeaturesSuggested || []
},
extensions,
userAgent: getUserAgent(),
clientServices, // TODO: maybe combine this with extensions except version support then needs to be fixed in later step
Expand All @@ -54,12 +61,18 @@ export const Discovery = ({
const [lastUsed, _] = useLocalStorage(LOCAL_STORAGE_KEYS.LAST_INSTALLED, null)
const services = sortByAddress(data, lastUsed)

const isFeaturesSupported = isGreaterThanOrEqualToVersion(
appVersion,
SUPPORTED_VERSIONS.SUGGESTED_FEATURES
)

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

return (
<Container paddingTop={5} paddingBottom={5}>
<Header />
{isFeaturesSupported && <Features />}
<Stack spacing="12px">
{services.length === 0 && <div>No Wallets Found</div>}
{services.map((service, index) => {
Expand Down
32 changes: 32 additions & 0 deletions components/Features.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Box, HStack, Tag, Text, IconButton } from '@chakra-ui/react'
import { CheckIcon } from '@chakra-ui/icons'
import { useFCL } from '../hooks/useFCL'
import FEATURES_LIST from '../data/features.json'

export default function Features() {
const { clientConfig } = useFCL()
const featuresListKeys = FEATURES_LIST.map(f => f.name)
const suggestedFeatures = clientConfig?.discoveryFeaturesSuggested?.filter(f => featuresListKeys.includes(f)) || []

return (
<Box mb={5}>
<HStack mb={3}>
<Text fontSize='sm' as='b'>Wallet Requirements</Text>
<IconButton
isRound={true}
variant='solid'
colorScheme='teal'
aria-label='Done'
fontSize='sm'
size={'xs'}
icon={<CheckIcon />}
/>
</HStack>
<HStack>
{suggestedFeatures.map((suggestion, index) => (
<Tag key={index} size='sm'>{suggestion}</Tag>
))}
</HStack>
</Box>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,10 @@ exports[`Component: AppHeader should handle missing info and show unknown if no
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
gap: 0.5rem;
margin-bottom: 25px;
}

.emotion-0>*:not(style)~*:not(style) {
margin-top: 0.5rem;
-webkit-margin-end: 0px;
margin-inline-end: 0px;
margin-bottom: 0px;
-webkit-margin-start: 0px;
margin-inline-start: 0px;
}

.emotion-2 {
display: -webkit-box;
display: -webkit-flex;
Expand All @@ -33,15 +25,7 @@ exports[`Component: AppHeader should handle missing info and show unknown if no
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
}

.emotion-2>*:not(style)~*:not(style) {
margin-top: 0px;
-webkit-margin-end: 0px;
margin-inline-end: 0px;
margin-bottom: 0px;
-webkit-margin-start: 0.5rem;
margin-inline-start: 0.5rem;
gap: 0.5rem;
}

.emotion-3 {
Expand Down Expand Up @@ -77,18 +61,10 @@ exports[`Component: AppHeader should render the the component with icon 1`] = `
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
gap: 0.5rem;
margin-bottom: 25px;
}

.emotion-0>*:not(style)~*:not(style) {
margin-top: 0.5rem;
-webkit-margin-end: 0px;
margin-inline-end: 0px;
margin-bottom: 0px;
-webkit-margin-start: 0px;
margin-inline-start: 0px;
}

.emotion-2 {
display: -webkit-box;
display: -webkit-flex;
Expand All @@ -101,15 +77,7 @@ exports[`Component: AppHeader should render the the component with icon 1`] = `
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
}

.emotion-2>*:not(style)~*:not(style) {
margin-top: 0px;
-webkit-margin-end: 0px;
margin-inline-end: 0px;
margin-bottom: 0px;
-webkit-margin-start: 0.5rem;
margin-inline-start: 0.5rem;
gap: 0.5rem;
}

.emotion-3 {
Expand Down
40 changes: 4 additions & 36 deletions components/Headers/__tests__/__snapshots__/Header.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,10 @@ exports[`Component: Header should render the configurable component if version i
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
gap: 0.5rem;
margin-bottom: 25px;
}

.emotion-0>*:not(style)~*:not(style) {
margin-top: 0.5rem;
-webkit-margin-end: 0px;
margin-inline-end: 0px;
margin-bottom: 0px;
-webkit-margin-start: 0px;
margin-inline-start: 0px;
}

.emotion-2 {
display: -webkit-box;
display: -webkit-flex;
Expand All @@ -94,15 +86,7 @@ exports[`Component: Header should render the configurable component if version i
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
}

.emotion-2>*:not(style)~*:not(style) {
margin-top: 0px;
-webkit-margin-end: 0px;
margin-inline-end: 0px;
margin-bottom: 0px;
-webkit-margin-start: 0.5rem;
margin-inline-start: 0.5rem;
gap: 0.5rem;
}

.emotion-3 {
Expand Down Expand Up @@ -149,18 +133,10 @@ exports[`Component: Header should show the DeveloperMessage if config is missing
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
gap: 0.5rem;
margin-bottom: 25px;
}

.emotion-0>*:not(style)~*:not(style) {
margin-top: 0.5rem;
-webkit-margin-end: 0px;
margin-inline-end: 0px;
margin-bottom: 0px;
-webkit-margin-start: 0px;
margin-inline-start: 0px;
}

.emotion-2 {
display: -webkit-box;
display: -webkit-flex;
Expand All @@ -173,15 +149,7 @@ exports[`Component: Header should show the DeveloperMessage if config is missing
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
}

.emotion-2>*:not(style)~*:not(style) {
margin-top: 0px;
-webkit-margin-end: 0px;
margin-inline-end: 0px;
margin-bottom: 0px;
-webkit-margin-start: 0.5rem;
margin-inline-start: 0.5rem;
gap: 0.5rem;
}

.emotion-3 {
Expand Down
62 changes: 50 additions & 12 deletions components/ServiceCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,19 @@ import {
Flex,
HStack,
Icon,
IconButton,
Image,
Spacer,
Stack,
Tag,
Text,
} from '@chakra-ui/react'
import { FiInfo } from 'react-icons/fi'
import { Service } from '../types'
import { getProviderMetadataByAddress } from '../helpers/metadata'
import { CheckIcon } from '@chakra-ui/icons'
import { useMemo } from 'react'
import FEATURES_LIST from '../data/features.json'

type Props = {
isEnabled: boolean
Expand All @@ -34,7 +40,7 @@ export default function ServiceCard({
service,
lastUsed = false,
}: Props) {
const { appVersion } = useFCL()
const { appVersion, clientConfig } = useFCL()
const [_, setLastUsed] = useLocalStorage(
LOCAL_STORAGE_KEYS.LAST_INSTALLED,
null
Expand All @@ -44,6 +50,14 @@ export default function ServiceCard({
const installLink = service?.provider?.install_link
const isExtensionService = isExtension(service)
const isExtensionServiceInstalled = Boolean(service?.provider?.is_installed)
const supportedFeatures = getProviderMetadataByAddress(service?.provider?.address)?.features?.supported || []
const isFeaturesSupported = isGreaterThanOrEqualToVersion(
appVersion,
SUPPORTED_VERSIONS.SUGGESTED_FEATURES
)
const featuresListKeys = FEATURES_LIST.map(f => f.name)
const suggestedFeatures = clientConfig?.discoveryFeaturesSuggested?.filter(f => featuresListKeys.includes(f)) || []


const onSelect = () => {
if (!service) return
Expand All @@ -69,6 +83,11 @@ export default function ServiceCard({
}
}

const hasSuggestedFeatures = useMemo(() => {
return suggestedFeatures.every(feature => supportedFeatures.includes(feature))
}, [suggestedFeatures, supportedFeatures])


const openMoreInfo = e => {
e.stopPropagation()
if (!hasWebsite) return
Expand All @@ -88,17 +107,36 @@ export default function ServiceCard({
>
<CardBody width="100%">
<Flex alignItems="center" justifyContent="space-between">
<HStack>
<Image src={icon} alt={name} borderRadius="full" boxSize="3rem" />
<Text fontSize="lg" as="b">
{truncateString(name, 13)}
</Text>
{isExtensionService && !isExtensionServiceInstalled && (
<Tag size="sm">Install Extension</Tag>
)}
{lastUsed && <Tag size="sm">Last Used</Tag>}
</HStack>
<Spacer />
<Stack>
<Flex alignItems="center" justifyContent="space-between">
<HStack>
<Image src={icon} alt={name} borderRadius="full" boxSize="2.7rem" />
<Text fontSize="lg" as="b">
{truncateString(name, 10)}
</Text>
{isExtensionService && !isExtensionServiceInstalled && (
<Tag size="sm" colorScheme='cyan'>Install Extension</Tag>
)}
{lastUsed && <Tag size="sm" colorScheme='cyan'>Last Used</Tag>}
{isFeaturesSupported && hasSuggestedFeatures && (
<IconButton
isRound={true}
variant='solid'
colorScheme='teal'
aria-label='Done'
fontSize='sm'
size={'xs'}
icon={<CheckIcon />}
/>
)}
</HStack>
</Flex>
<HStack mt={2}>
{supportedFeatures.map((feature, index) => {
return <Tag key={index} size="sm" colorScheme='gray'>{feature}</Tag>
})}
</HStack>
</Stack>
{hasWebsite && (
<Box
color="lightgrey"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,12 @@ exports[`Component: DeveloperMessage should render the DeveloperMessage componen

<div
class="chakra-alert emotion-0"
data-status="warning"
role="alert"
>
<span
class="chakra-alert__icon emotion-1"
data-status="warning"
>
<svg
class="chakra-icon emotion-2"
Expand All @@ -72,11 +74,13 @@ exports[`Component: DeveloperMessage should render the DeveloperMessage componen
</span>
<div
class="chakra-alert__title emotion-3"
data-status="warning"
>
Missing Config
</div>
<div
class="chakra-alert__desc emotion-4"
data-status="warning"
>
See how to set your app title and icon

Expand Down
14 changes: 14 additions & 0 deletions data/features.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"name": "self-custody",
"description": "Users manage their own keys, giving them full control of their crypto assets. Safekeeping responsibility rests with the user."
},
{
"name": "hardware",
"description": "Keys are stored on a physical device, apart from online devices. This enhances security as keys are exposed online only during transactions."
},
{
"name": "custodial",
"description": "Keys are held by the wallet. It's convenient but relies on the wallet's trustworthiness, and users don't have full control."
}
]
Loading
Loading