Skip to content

Commit

Permalink
feat: federation in the wallet (#238)
Browse files Browse the repository at this point in the history
Signed-off-by: Jan <[email protected]>
Co-authored-by: Timo Glastra <[email protected]>
  • Loading branch information
janrtvld and TimoGlastra authored Nov 26, 2024
1 parent a16b1ef commit 03cecfb
Show file tree
Hide file tree
Showing 15 changed files with 199 additions and 224 deletions.
4 changes: 2 additions & 2 deletions apps/easypid/src/app/(app)/_layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Redirect, Stack, useGlobalSearchParams, useLocalSearchParams, usePathname, useRouter } from 'expo-router'
import { Redirect, Stack, useGlobalSearchParams, usePathname, useRouter } from 'expo-router'

import { TypedArrayEncoder } from '@credo-ts/core'
import { useSecureUnlock } from '@easypid/agent'
Expand Down Expand Up @@ -135,7 +135,7 @@ export default function AppLayout() {
<Stack.Screen name="activity/[id]" options={headerNormalOptions} />
<Stack.Screen name="pinConfirmation" options={headerNormalOptions} />
<Stack.Screen name="pinLocked" options={headerNormalOptions} />
<Stack.Screen name="issuer" options={headerNormalOptions} />
<Stack.Screen name="federation" options={headerNormalOptions} />
<Stack.Screen name="pidSetup" />
</Stack>
</WalletJsonStoreProvider>
Expand Down
18 changes: 18 additions & 0 deletions apps/easypid/src/app/(app)/federation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { FunkeFederationDetailScreen } from '@easypid/features/wallet/FunkeFederationDetailScreen'
import type { TrustedEntity } from '@package/agent'
import { useLocalSearchParams } from 'expo-router'

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

const trustedEntitiesArray = JSON.parse(decodeURIComponent(trustedEntities as string)) as Array<TrustedEntity>

return (
<FunkeFederationDetailScreen
entityId={entityId as string}
trustedEntities={trustedEntitiesArray}
name={name as string}
logo={logo as string}
/>
)
}
8 changes: 0 additions & 8 deletions apps/easypid/src/app/(app)/issuer.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,6 @@ export function FunkeCredentialNotificationScreen() {
logo={credentialDisplay.issuer.logo}
entityId={issuerMetadata?.credential_issuer as string}
lastInteractionDate={activities[0]?.date}
approvalsCount={0}
onContinue={onCheckCardContinue}
/>
),
Expand Down
25 changes: 16 additions & 9 deletions apps/easypid/src/features/receive/slides/VerifyPartySlide.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { DisplayImage } from '@package/agent'
import type { DisplayImage, TrustedEntity } from '@package/agent'

import {
Circle,
Expand All @@ -25,8 +25,8 @@ interface VerifyPartySlideProps {
logo?: DisplayImage
backgroundColor?: string
lastInteractionDate?: string
approvalsCount?: number
onContinue?: () => Promise<void>
trustedEntities?: Array<TrustedEntity>
}

export const VerifyPartySlide = ({
Expand All @@ -36,8 +36,8 @@ export const VerifyPartySlide = ({
logo,
backgroundColor,
lastInteractionDate,
approvalsCount,
onContinue,
trustedEntities,
}: 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(
`/federation?name=${encodeURIComponent(name ?? '')}&logo=${encodeURIComponent(logo?.url ?? '')}&entityId=${encodeURIComponent(entityId)}&trustedEntities=${encodeURIComponent(JSON.stringify(trustedEntities ?? []))}`
)
})

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

<YStack gap="$4">
{approvalsCount ? (
{trustedEntities && trustedEntities.length > 0 ? (
<InfoButton
variant="positive"
title="Verified organisation"
description={`Approved by ${approvalsCount} organisations`}
variant="info"
title="Recognized organisation"
description={`Approved by ${trustedEntities.length} organisations`}
onPress={onPressVerifiedIssuer}
/>
) : (
<InfoButton variant="unknown" title="Unverified organization" description="No trust approvals found" />
<InfoButton
variant="warning"
title="Unrecognized organisation"
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 @@ -122,6 +122,7 @@ export function FunkeMdocOfflineSharingScreen({
hostName: undefined,
logo: undefined,
name: 'Unknown party',
trustedEntities: [],
},
},
status
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}
trustedEntities={credentialsForRequest?.verifier.trustedEntities}
lastInteractionDate={lastInteractionDate}
approvalsCount={fedDisplayData?.approvals.length}
onComplete={() => pushToWallet('replace')}
/>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { DisplayImage, FormattedSubmission } from '@package/agent'
import type { DisplayImage, FormattedSubmission, TrustedEntity } from '@package/agent'

import { type SlideStep, SlideWizard } from '@package/app'
import { LoadingRequestSlide } from '../receive/slides/LoadingRequestSlide'
Expand All @@ -13,8 +13,7 @@ interface FunkePresentationNotificationScreenProps {
verifierName?: string
logo?: DisplayImage
lastInteractionDate?: string
approvalsCount?: number

trustedEntities?: Array<TrustedEntity>
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,
trustedEntities,
}: FunkePresentationNotificationScreenProps) {
return (
<SlideWizard
Expand All @@ -57,7 +56,7 @@ export function FunkePresentationNotificationScreen({
name={verifierName}
logo={logo}
lastInteractionDate={lastInteractionDate}
approvalsCount={approvalsCount}
trustedEntities={trustedEntities}
/>
),
},
Expand Down
104 changes: 104 additions & 0 deletions apps/easypid/src/features/wallet/FunkeFederationDetailScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import type { TrustedEntity } from '@package/agent'
import {
Circle,
FlexPage,
Heading,
HeroIcons,
IconContainer,
Image,
MessageBox,
Paragraph,
ScrollView,
type ScrollViewRefType,
XStack,
YStack,
} from '@package/ui'
import { TextBackButton, useScrollViewPosition } from 'packages/app/src'
import React, { useRef } from 'react'
import { useSafeAreaInsets } from 'react-native-safe-area-context'

interface FunkeFederationDetailScreenProps {
name: string
logo?: string
entityId?: string
trustedEntities?: Array<TrustedEntity>
}

export function FunkeFederationDetailScreen({
name,
logo,
entityId,
trustedEntities = [],
}: FunkeFederationDetailScreenProps) {
const { handleScroll, isScrolledByOffset, scrollEventThrottle } = useScrollViewPosition()
const { bottom } = useSafeAreaInsets()
const scrollViewRef = useRef<ScrollViewRefType>(null)

return (
<FlexPage gap="$0" paddingHorizontal="$0">
<YStack
w="100%"
top={0}
p="$4"
borderBottomWidth="$0.5"
borderColor={isScrolledByOffset ? '$grey-200' : '$background'}
/>
<ScrollView ref={scrollViewRef} onScroll={handleScroll} scrollEventThrottle={scrollEventThrottle}>
<YStack gap="$4" p="$4" marginBottom={bottom}>
<Heading variant="h1">About this party</Heading>
<MessageBox
variant="light"
message="Always consider whether sharing with a party is wise."
icon={<HeroIcons.ExclamationTriangleFilled />}
/>
<XStack gap="$4" pt="$2" ai="center">
{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>
)}
<Heading variant="h2">{name}</Heading>
</XStack>
<YStack gap="$4" py="$2">
<YStack gap="$2">
<Heading variant="sub2">Trusted by</Heading>
<Paragraph>
{trustedEntities.length > 0 ? (
<>A list of organizations and whether they have approved {name}.</>
) : (
<>There are no organizations that have approved {name}.</>
)}
</Paragraph>
</YStack>
<YStack gap="$2">
{trustedEntities.map((entity) => {
return (
<XStack ai="center" key={entity.entity_id} br="$8" p="$3.5" gap="$3" bg="$grey-100">
{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" ai="center">
<Heading f={1} numberOfLines={2} variant="h2">
{entity.organization_name}
</Heading>
<IconContainer icon={<HeroIcons.CheckCircleFilled size={30} color="$positive-500" />} />
</XStack>
</XStack>
)
})}
</YStack>
</YStack>
</YStack>
</ScrollView>
<YStack btw="$0.5" borderColor="$grey-200" pt="$4" mx="$-4" px="$4" bg="$background">
<TextBackButton />
</YStack>
</FlexPage>
)
}
Loading

0 comments on commit 03cecfb

Please sign in to comment.