Skip to content

Commit

Permalink
feat: federation in the wallet
Browse files Browse the repository at this point in the history
Signed-off-by: Jan <[email protected]>
  • Loading branch information
janrtvld committed Nov 26, 2024
1 parent 8c855ce commit 697ff0c
Show file tree
Hide file tree
Showing 11 changed files with 193 additions and 153 deletions.
13 changes: 11 additions & 2 deletions apps/easypid/src/app/(app)/issuer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@ import { FunkeIssuerDetailScreen } from '@easypid/features/wallet/FunkeIssuerDet
import { useLocalSearchParams } from 'expo-router'

export default function Screen() {
const { host } = useLocalSearchParams()
const { entityId, trustedEntityIds, name, logo } = useLocalSearchParams()

return <FunkeIssuerDetailScreen host={host as string} />
const trustedEntityIdsArray = Array.isArray(trustedEntityIds) ? trustedEntityIds : trustedEntityIds?.split(',') ?? []

return (
<FunkeIssuerDetailScreen
entityId={entityId as string}
trustedEntityIds={trustedEntityIdsArray}
name={name as string}
logo={logo as string}
/>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -352,8 +352,8 @@ export function FunkeCredentialNotificationScreen() {
logo={credentialDisplay.issuer.logo}
entityId={issuerMetadata?.credential_issuer as string}
lastInteractionDate={activities[0]?.date}
approvalsCount={0}
onContinue={onCheckCardContinue}
trustedEntityIds={Object.keys(credentialsForRequest?.verifier.verifiedEntityIds ?? [])}
/>
),
},
Expand Down
21 changes: 14 additions & 7 deletions apps/easypid/src/features/receive/slides/VerifyPartySlide.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ interface VerifyPartySlideProps {
logo?: DisplayImage
backgroundColor?: string
lastInteractionDate?: string
approvalsCount?: number
onContinue?: () => Promise<void>
trustedEntityIds?: string[]
}

export const VerifyPartySlide = ({
Expand All @@ -36,8 +36,8 @@ export const VerifyPartySlide = ({
logo,
backgroundColor,
lastInteractionDate,
approvalsCount,
onContinue,
trustedEntityIds,
}: VerifyPartySlideProps) => {
const router = useRouter()
const { onNext, onCancel } = useWizard()
Expand All @@ -54,7 +54,9 @@ export const VerifyPartySlide = ({
}

const onPressVerifiedIssuer = withHaptics(() => {
router.push(`/issuer?entityId=${entityId}`)
router.push(
`/issuer?name=${name}&logo=${logo?.url}&entityId=${entityId}&trustedEntityIds=${trustedEntityIds?.join(',') ?? ''}`
)
})

const onPressInteraction = withHaptics(() => {
Expand Down Expand Up @@ -93,15 +95,20 @@ export const VerifyPartySlide = ({
</YStack>

<YStack gap="$4">
{approvalsCount ? (
{trustedEntityIds && trustedEntityIds.length > 0 ? (
<InfoButton
variant="positive"
variant="unknown"
title="Verified organisation"
description={`Approved by ${approvalsCount} organisations`}
description={`Approved by ${trustedEntityIds?.length} organisations`}
onPress={onPressVerifiedIssuer}
/>
) : (
<InfoButton variant="unknown" title="Unverified organization" description="No trust approvals found" />
<InfoButton
variant="unknown"
title="Unverified organization"
description="No trust approvals found"
onPress={onPressVerifiedIssuer}
/>
)}
<InfoButton
variant={lastInteractionDate ? 'interaction-success' : 'interaction-new'}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { useLocalSearchParams } from 'expo-router'
import React, { useEffect, useState, useMemo, useCallback } from 'react'

import { useAppAgent } from '@easypid/agent'
import { getOpenIdFedIssuerMetadata } from '@easypid/utils/issuer'
import { usePushToWallet } from '@package/app/src/hooks/usePushToWallet'
import { setWalletServiceProviderPin } from '../../crypto/WalletServiceProviderClient'
import { useShouldUsePinForSubmission } from '../../hooks/useShouldUsePinForPresentation'
Expand All @@ -30,15 +29,8 @@ export function FunkeOpenIdPresentationNotificationScreen() {
const { activities } = useActivities({
filters: { entityId: credentialsForRequest?.verifier.entityId ?? 'NO MATCH' },
})
const shouldUsePin = useShouldUsePinForSubmission(credentialsForRequest)

// TODO: this should be returnd by getCredentialsForProofRequest
// TODO: addSharedActivityForCredentialsForRequest should take into account fed display metadata
const fedDisplayData = useMemo(
() => credentialsForRequest && getOpenIdFedIssuerMetadata(credentialsForRequest.verifier.entityId),
[credentialsForRequest]
)
const lastInteractionDate = activities?.[0]?.date
const shouldUsePin = useShouldUsePinForSubmission(credentialsForRequest)

useEffect(() => {
if (credentialsForRequest) return
Expand Down Expand Up @@ -154,8 +146,8 @@ export function FunkeOpenIdPresentationNotificationScreen() {
entityId={credentialsForRequest?.verifier.entityId as string}
verifierName={credentialsForRequest?.verifier.name}
logo={credentialsForRequest?.verifier.logo}
trustedEntityIds={Object.keys(credentialsForRequest?.verifier.verifiedEntityIds ?? [])}
lastInteractionDate={lastInteractionDate}
approvalsCount={fedDisplayData?.approvals.length}
onComplete={() => pushToWallet('replace')}
/>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ interface FunkePresentationNotificationScreenProps {
verifierName?: string
logo?: DisplayImage
lastInteractionDate?: string
approvalsCount?: number

trustedEntityIds?: string[]
submission?: FormattedSubmission
usePin: boolean
isAccepting: boolean
Expand All @@ -28,13 +27,13 @@ export function FunkePresentationNotificationScreen({
verifierName,
logo,
lastInteractionDate,
approvalsCount,
usePin,
onAccept,
onDecline,
isAccepting,
submission,
onComplete,
trustedEntityIds,
}: FunkePresentationNotificationScreenProps) {
return (
<SlideWizard
Expand All @@ -57,7 +56,7 @@ export function FunkePresentationNotificationScreen({
name={verifierName}
logo={logo}
lastInteractionDate={lastInteractionDate}
approvalsCount={approvalsCount}
trustedEntityIds={trustedEntityIds}
/>
),
},
Expand Down
127 changes: 60 additions & 67 deletions apps/easypid/src/features/wallet/FunkeIssuerDetailScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,53 +1,36 @@
import { getOpenIdFedIssuerMetadata } from '@easypid/utils/issuer'
import {
Circle,
FlexPage,
Heading,
HeroIcons,
IconContainer,
Image,
MessageBox,
Paragraph,
ScrollView,
type ScrollViewRefType,
Stack,
XStack,
YStack,
useToastController,
} from '@package/ui'
import { useRouter } from 'expo-router'
import { TextBackButton, useHaptics, useScrollViewPosition } from 'packages/app/src'
import { useTrustedEntities } from 'packages/agent/src'
import { TextBackButton, useScrollViewPosition } from 'packages/app/src'
import { useRef } from 'react'
import { Linking } from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context'

interface FunkeIssuerDetailScreenProps {
host: string
name: string
logo?: string
entityId?: string
trustedEntityIds?: string[]
}

export function FunkeIssuerDetailScreen({ host }: FunkeIssuerDetailScreenProps) {
const toast = useToastController()
const router = useRouter()
const { withHaptics, errorHaptic } = useHaptics()
const data = getOpenIdFedIssuerMetadata(host)

if (!data) {
router.back()
errorHaptic()
return toast.show('Currently unavailable.', {
customData: {
preset: 'warning',
},
})
}
export function FunkeIssuerDetailScreen({ name, logo, entityId, trustedEntityIds = [] }: FunkeIssuerDetailScreenProps) {
const { trustedEntities } = useTrustedEntities()

const { handleScroll, isScrolledByOffset, scrollEventThrottle } = useScrollViewPosition()
const { bottom } = useSafeAreaInsets()
const scrollViewRef = useRef<ScrollViewRefType>(null)

const openDomain = withHaptics(() => {
Linking.openURL(`https://${host}`)
})

return (
<FlexPage gap="$0" paddingHorizontal="$0">
<YStack
Expand All @@ -66,56 +49,66 @@ export function FunkeIssuerDetailScreen({ host }: FunkeIssuerDetailScreenProps)
icon={<HeroIcons.ExclamationTriangleFilled />}
/>
<XStack gap="$4" pt="$2">
<Circle overflow="hidden" size="$5" bg="$grey-100">
<Image src={data.display.logo.url} height="100%" width="100%" />
</Circle>
{logo ? (
<Circle overflow="hidden" ai="center" jc="center" size="$6" bw={1} borderColor="$grey-200" bg="$grey-100">
<Image src={logo} height="100%" width="100%" />
</Circle>
) : (
<Circle overflow="hidden" ai="center" jc="center" size="$6" bw={1} borderColor="$grey-200" bg="$grey-100">
<HeroIcons.BuildingOffice color="$grey-700" />
</Circle>
)}
<YStack>
<Heading variant="h2">{data.display.name}</Heading>
<Paragraph onPress={openDomain} fontWeight="$medium" color="$primary-500">
{host}
<Heading variant="h2">{name}</Heading>
<Paragraph fontWeight="$medium" color="$primary-500">
Website
</Paragraph>
</YStack>
</XStack>
<YStack gap="$4" py="$2">
<YStack gap="$2">
<Heading variant="sub2">Approvals</Heading>
<Paragraph>A list of entities that have approved {data.display.name}.</Paragraph>
</YStack>
<YStack gap="$2">
{data.approvals.map((approval) => (
<XStack
ai="center"
key={approval.id}
br="$8"
p="$3.5"
gap="$3"
bw="$0.5"
borderColor="$grey-200"
bg="$white"
>
<Circle overflow="hidden" size="$4" bg="$grey-50">
<Image src={approval.ownedBy.display.logo.url} height="100%" width="100%" />
</Circle>
<YStack gap="$1" f={1}>
<Heading variant="h3">{approval.name}</Heading>
<Paragraph fontSize={15}>Owned by {approval.ownedBy.display.name}</Paragraph>
</YStack>
</XStack>
))}
<Heading variant="sub2">Trusted by</Heading>
<Paragraph>
A list of organizations and whether they have approved{' '}
<Paragraph fontWeight="$semiBold">{name}</Paragraph>.
</Paragraph>
</YStack>
</YStack>
<YStack gap="$4" py="$2">
<YStack gap="$2">
<Heading variant="sub2">Trust marks</Heading>
<Paragraph>Certifications that verify {data.display.name}'s security and quality standards.</Paragraph>
{trustedEntities.map((entity) => {
const isTrusted = trustedEntityIds.includes(entity.entity_id)

return (
<XStack
ai="center"
key={entity.entity_id}
br="$8"
p="$3.5"
gap="$3"
bw="$0.5"
borderColor={isTrusted ? '$success-500' : '$danger-300'}
bg={isTrusted ? '$success-500' : '$danger-300'}
>
{entity.logo_uri && (
<Circle overflow="hidden" size="$4" bg="$grey-50">
<Image src={entity.logo_uri} height="100%" width="100%" />
</Circle>
)}
<XStack gap="$1" f={1} justifyContent="space-between">
<Heading variant="h2">{entity.organization_name}</Heading>
<IconContainer
icon={
isTrusted ? (
<HeroIcons.CheckCircleFilled color="$success-500" />
) : (
<HeroIcons.X color="$danger-500" />
)
}
/>
</XStack>
</XStack>
)
})}
</YStack>
<XStack flexWrap="wrap" gap="$2">
{data.certifications.map((certification) => (
<Stack key={certification} br="$12" p="$2" px="$4" bg="$grey-100">
<Paragraph fontWeight="$medium">{certification}</Paragraph>
</Stack>
))}
</XStack>
</YStack>
</YStack>
</ScrollView>
Expand Down
52 changes: 0 additions & 52 deletions apps/easypid/src/utils/issuer.ts

This file was deleted.

Loading

0 comments on commit 697ff0c

Please sign in to comment.