diff --git a/apps/easypid/src/features/menu/FunkeAboutScreen.tsx b/apps/easypid/src/features/menu/FunkeAboutScreen.tsx index beaf3533..b8f245e3 100644 --- a/apps/easypid/src/features/menu/FunkeAboutScreen.tsx +++ b/apps/easypid/src/features/menu/FunkeAboutScreen.tsx @@ -4,11 +4,22 @@ import React from 'react' import { TextBackButton } from 'packages/app' import { Linking } from 'react-native' +import { useIsFunkeWallet } from '@easypid/hooks/useFeatureFlag' import pj from '../../../package.json' +const TEXT_FUNKE = `This app was created by Animo Solutions in the context of the SPRIN-D Funke ‘EUDI Wallet Prototypes’. It + serves as a prototype for future wallet providers. All code is available under Apache 2.0.` + +const TEXT_PARADYM = + 'This app was created by Animo Solutions as a companion app for Paradym. All code is available under Apache 2.0.' + export function FunkeAboutScreen() { + const isFunkeWallet = useIsFunkeWallet() + const openContact = () => { - Linking.openURL('mailto:ana@animo.id?subject=Reach out from Funke EUDI Wallet') + Linking.openURL( + `mailto:ana@animo.id?subject=Reach out from ${isFunkeWallet ? 'Funke EUDI Wallet' : 'Paradym Wallet'}` + ) } const openPrivacyPolicy = () => { @@ -20,15 +31,13 @@ export function FunkeAboutScreen() { - - This app was created by Animo Solutions in the context of the SPRIN-D Funke ‘EUDI Wallet Prototypes’. It - serves as a prototype for future wallet providers. All code is available under Apache 2.0. - + {isFunkeWallet ? TEXT_FUNKE : TEXT_PARADYM} - For more information on the project visit sprind.org or reach out to{' '} + For more information, reach out to{' '} ana@animo.id + . @@ -40,7 +49,7 @@ export function FunkeAboutScreen() { onPress={openPrivacyPolicy} /> - EasyPID version: {pj.version} + Paradym Wallet version: {pj.version} diff --git a/apps/easypid/src/features/menu/FunkeMenuScreen.tsx b/apps/easypid/src/features/menu/FunkeMenuScreen.tsx index 826e1b79..135e0364 100644 --- a/apps/easypid/src/features/menu/FunkeMenuScreen.tsx +++ b/apps/easypid/src/features/menu/FunkeMenuScreen.tsx @@ -15,6 +15,7 @@ import { } from '@package/ui' import { usePidCredential } from '@easypid/hooks' +import { useFeatureFlag } from '@easypid/hooks/useFeatureFlag' import { useWalletReset } from '@easypid/hooks/useWalletReset' import { TextBackButton } from '@package/app' import { Link } from 'expo-router' @@ -53,28 +54,31 @@ export function FunkeMenuScreen() { const { handleScroll, isScrolledByOffset, scrollEventThrottle } = useScrollViewPosition() const onResetWallet = useWalletReset() const { credential } = usePidCredential() + const hasEidCardFeatureFlag = useFeatureFlag('EID_CARD') - const idItem = credential ? ( - - ) : ( - - ) + const idItem = hasEidCardFeatureFlag ? ( + credential ? ( + + ) : ( + + ) + ) : null return ( diff --git a/apps/easypid/src/features/onboarding/onboardingContext.tsx b/apps/easypid/src/features/onboarding/onboardingContext.tsx index 820bd686..88c55aa5 100644 --- a/apps/easypid/src/features/onboarding/onboardingContext.tsx +++ b/apps/easypid/src/features/onboarding/onboardingContext.tsx @@ -2,6 +2,7 @@ import { sendCommand } from '@animo-id/expo-ausweis-sdk' import type { SdJwtVcHeader } from '@credo-ts/core' import { type AppAgent, initializeAppAgent, useSecureUnlock } from '@easypid/agent' import { setWalletServiceProviderPin } from '@easypid/crypto/WalletServiceProviderClient' +import { useFeatureFlag } from '@easypid/hooks/useFeatureFlag' import { ReceivePidUseCaseCFlow } from '@easypid/use-cases/ReceivePidUseCaseCFlow' import type { CardScanningErrorDetails, @@ -14,7 +15,6 @@ import { type OnboardingPage, type OnboardingStep, SIMULATOR_PIN, - pidSetupSteps, } from '@easypid/utils/sharedPidSetup' import { BiometricAuthenticationCancelledError, @@ -34,99 +34,9 @@ import { Linking, Platform } from 'react-native' import type { PidSdJwtVcAttributes } from '../../hooks' import { addReceivedActivity } from '../activity/activityRecord' import { useHasFinishedOnboarding } from './hasFinishedOnboarding' -import { OnboardingBiometrics } from './screens/biometrics' -import { OnboardingIntroductionSteps } from './screens/introduction-steps' -import OnboardingPinEnter from './screens/pin' -import { OnboardingWalletExplanation } from './screens/wallet-explanation' -import OnboardingWelcome from './screens/welcome' +import { onboardingSteps } from './steps' import { useShouldUseCloudHsm } from './useShouldUseCloudHsm' -export const onboardingSteps = [ - { - step: 'welcome', - alternativeFlow: false, - progress: 0, - page: { - type: 'fullscreen', - }, - Screen: OnboardingWelcome, - }, - { - step: 'wallet-explanation', - alternativeFlow: false, - progress: 0.1, - page: { - animation: 'delayed', - type: 'content', - title: '', - }, - Screen: OnboardingWalletExplanation, - }, - - { - step: 'introduction-steps', - alternativeFlow: false, - progress: 20, - page: { - animation: 'delayed', - type: 'content', - title: 'Set up your wallet', - subtitle: 'Before you can use the app, we will guide you through these steps.', - }, - Screen: OnboardingIntroductionSteps, - }, - - { - step: 'pin', - alternativeFlow: false, - progress: 30, - page: { - type: 'content', - title: 'Choose a 6-digit PIN', - subtitle: 'This PIN secures your identity wallet. You enter it every time you share data.', - animationKey: 'pin', - }, - Screen: OnboardingPinEnter, - }, - { - step: 'pin-reenter', - alternativeFlow: false, - progress: 30, - page: { - type: 'content', - title: 'Repeat your PIN', - subtitle: 'This PIN secures your identity wallet. You enter it every time you share data.', - animationKey: 'pin', - }, - Screen: OnboardingPinEnter, - }, - { - step: 'biometrics', - alternativeFlow: false, - progress: 40, - page: { - type: 'content', - title: 'Set up biometrics', - subtitle: - 'Activate the biometrics functionality of your phone to make sure only you can enter your wallet and share data.', - }, - Screen: OnboardingBiometrics, - }, - { - step: 'biometrics-disabled', - progress: 40, - alternativeFlow: true, - page: { - type: 'content', - title: 'You need to enable biometrics', - subtitle: - 'To continue, make sure your device has biometric protection enabled, and that EasyPID is allowed to use biometrics.', - }, - Screen: OnboardingBiometrics, - }, - ...pidSetupSteps, -] as const satisfies Array - export type OnboardingContext = { currentStep: OnboardingStep['step'] progress: number @@ -166,6 +76,7 @@ export function OnboardingContextProvider({ showScanModal: true, }) const [eidCardRequestedAccessRights, setEidCardRequestedAccessRights] = useState() + const hasEidCardFeatureFlag = useFeatureFlag('EID_CARD') const currentStep = onboardingSteps.find((step) => step.step === currentStepName) if (!currentStep) throw new Error(`Invalid step ${currentStepName}`) @@ -236,7 +147,7 @@ export function OnboardingContextProvider({ // Allows bypassing the eID card and use a simulator card const isSimulatorPinCode = pin === SIMULATOR_PIN - if (isSimulatorPinCode) { + if (isSimulatorPinCode && hasEidCardFeatureFlag) { toast.show('Simulator eID card activated', { customData: { preset: 'success', @@ -677,7 +588,7 @@ export function OnboardingContextProvider({ } else if (currentStep.step === 'biometrics') { screen = } else if (currentStep.step === 'biometrics-disabled') { - screen = + screen = } else if (currentStep.step === 'data-protection') { screen = } else if (currentStep.step === 'id-card-requested-attributes') { diff --git a/apps/easypid/src/features/onboarding/screens/welcome.tsx b/apps/easypid/src/features/onboarding/screens/welcome.tsx index 00081121..ebd5c266 100644 --- a/apps/easypid/src/features/onboarding/screens/welcome.tsx +++ b/apps/easypid/src/features/onboarding/screens/welcome.tsx @@ -1,18 +1,5 @@ -import { - Blob, - Button, - FlexPage, - Heading, - HeroIcons, - IconContainer, - Image, - Paragraph, - Stack, - XStack, - YStack, -} from '@package/ui' +import { Blob, Button, FlexPage, Heading, Image, Paragraph, Stack, XStack, YStack } from '@package/ui' import type React from 'react' -import { Alert } from 'react-native' import appIcon from '../../../../assets/icon.png' @@ -28,43 +15,33 @@ export default function OnboardingWelcome({ goToNextStep }: OnboardingWelcomePro - + + + - - - - } - onPress={() => { - Alert.alert( - 'This is the EasyPID wallet', - '\nThis is your digital wallet. With it, you can store and share information about yourself.' - ) - }} - /> - - - - Paradym Wallet - - This is your digital wallet. With it, you can store and share information about yourself. - - - - - Get Started - - + + + + Paradym Wallet + + This is your digital wallet. With it, you can store and share information about yourself. + + + + Get Started + + ) diff --git a/apps/easypid/src/features/onboarding/steps.ts b/apps/easypid/src/features/onboarding/steps.ts new file mode 100644 index 00000000..773fd95d --- /dev/null +++ b/apps/easypid/src/features/onboarding/steps.ts @@ -0,0 +1,155 @@ +import { useFeatureFlag } from '@easypid/hooks/useFeatureFlag' +import { type OnboardingStep, pidSetupSteps } from '@easypid/utils/sharedPidSetup' +import { OnboardingBiometrics } from './screens/biometrics' +import { OnboardingIntroductionSteps } from './screens/introduction-steps' +import OnboardingPinEnter from './screens/pin' +import { OnboardingWalletExplanation } from './screens/wallet-explanation' +import OnboardingWelcome from './screens/welcome' + +export const onboardingSteps = useFeatureFlag('EID_CARD') + ? ([ + { + step: 'welcome', + alternativeFlow: false, + progress: 0, + page: { + type: 'fullscreen', + }, + Screen: OnboardingWelcome, + }, + { + step: 'wallet-explanation', + alternativeFlow: false, + progress: 0.1, + page: { + animation: 'delayed', + type: 'content', + title: '', + }, + Screen: OnboardingWalletExplanation, + }, + { + step: 'introduction-steps', + alternativeFlow: false, + progress: 20, + page: { + animation: 'delayed', + type: 'content', + title: 'Set up your wallet', + subtitle: 'Before you can use the app, we will guide you through these steps.', + }, + Screen: OnboardingIntroductionSteps, + }, + + { + step: 'pin', + alternativeFlow: false, + progress: 30, + page: { + type: 'content', + title: 'Choose a 6-digit PIN', + subtitle: 'This PIN secures your identity wallet. You enter it every time you share data.', + animationKey: 'pin', + }, + Screen: OnboardingPinEnter, + }, + { + step: 'pin-reenter', + alternativeFlow: false, + progress: 30, + page: { + type: 'content', + title: 'Repeat your PIN', + subtitle: 'This PIN secures your identity wallet. You enter it every time you share data.', + animationKey: 'pin', + }, + Screen: OnboardingPinEnter, + }, + { + step: 'biometrics', + alternativeFlow: false, + progress: 40, + page: { + type: 'content', + title: 'Set up biometrics', + subtitle: + 'Activate the biometrics functionality of your phone to make sure only you can enter your wallet and share data.', + }, + Screen: OnboardingBiometrics, + }, + { + step: 'biometrics-disabled', + progress: 40, + alternativeFlow: true, + page: { + type: 'content', + title: 'You need to enable biometrics', + subtitle: + 'To continue, make sure your device has biometric protection enabled, and that EasyPID is allowed to use biometrics.', + animation: 'delayed', + }, + Screen: OnboardingBiometrics, + }, + ...pidSetupSteps, + ] as const satisfies Array) + : ([ + { + step: 'welcome', + alternativeFlow: false, + progress: 0, + page: { + type: 'fullscreen', + }, + Screen: OnboardingWelcome, + }, + { + step: 'pin', + alternativeFlow: false, + progress: 33, + page: { + type: 'content', + title: 'Choose a 6-digit PIN', + subtitle: 'This PIN secures your wallet. You enter it every time you share data.', + animationKey: 'pin', + animation: 'delayed', + }, + Screen: OnboardingPinEnter, + }, + { + step: 'pin-reenter', + alternativeFlow: false, + progress: 33, + page: { + type: 'content', + title: 'Repeat your PIN', + subtitle: 'This PIN secures your wallet. You enter it every time you share data.', + animationKey: 'pin', + }, + Screen: OnboardingPinEnter, + }, + { + step: 'biometrics', + alternativeFlow: false, + progress: 66, + page: { + type: 'content', + title: 'Set up biometrics', + subtitle: + 'Activate the biometrics functionality of your phone to make sure only you can enter your wallet and share data.', + }, + Screen: OnboardingBiometrics, + }, + { + step: 'biometrics-disabled', + progress: 66, + alternativeFlow: true, + page: { + type: 'content', + title: 'You need to enable biometrics', + animation: 'delayed', + subtitle: + 'To continue, make sure your device has biometric protection enabled, and that Paradym Wallet is allowed to use biometrics.', + }, + Screen: OnboardingBiometrics, + }, + ] as const satisfies Array) diff --git a/apps/easypid/src/features/pid/FunkePidSetupScreen.tsx b/apps/easypid/src/features/pid/FunkePidSetupScreen.tsx index be05f48d..7138e227 100644 --- a/apps/easypid/src/features/pid/FunkePidSetupScreen.tsx +++ b/apps/easypid/src/features/pid/FunkePidSetupScreen.tsx @@ -3,6 +3,7 @@ import { type SdJwtVcHeader, SdJwtVcRecord } from '@credo-ts/core' import { useSecureUnlock } from '@easypid/agent' import { InvalidPinError } from '@easypid/crypto/error' import type { PidSdJwtVcAttributes } from '@easypid/hooks' +import { useFeatureFlag } from '@easypid/hooks/useFeatureFlag' import { ReceivePidUseCaseCFlow } from '@easypid/use-cases/ReceivePidUseCaseCFlow' import type { CardScanningErrorDetails, @@ -37,6 +38,7 @@ export function FunkePidSetupScreen() { const toast = useToastController() const pushToWallet = usePushToWallet() const secureUnlock = useSecureUnlock() + const hasEidCardFeatureFlag = useFeatureFlag('EID_CARD') const [idCardPin, setIdCardPin] = useState() const [receivePidUseCase, setReceivePidUseCase] = useState() @@ -123,6 +125,17 @@ export function FunkePidSetupScreen() { onEnterPinRef.current.onEnterPin = onEnterPin }, [onEnterPin]) + useEffect(() => { + if (!hasEidCardFeatureFlag) { + toast.show('This feature is not supported in this version of the app.', { customData: { preset: 'warning' } }) + pushToWallet() + } + }, [hasEidCardFeatureFlag, toast, pushToWallet]) + + if (!hasEidCardFeatureFlag) { + return null + } + const onIdCardStart = async ({ walletPin, allowSimulatorCard, diff --git a/apps/easypid/src/features/wallet/FunkeWalletScreen.tsx b/apps/easypid/src/features/wallet/FunkeWalletScreen.tsx index 1b21e57f..adbe5195 100644 --- a/apps/easypid/src/features/wallet/FunkeWalletScreen.tsx +++ b/apps/easypid/src/features/wallet/FunkeWalletScreen.tsx @@ -10,6 +10,7 @@ import { Paragraph, ScrollView, Spacer, + Stack, XStack, YStack, useSpringify, @@ -18,6 +19,7 @@ import { import { useRouter } from 'expo-router' import { useFirstNameFromPidCredential } from '@easypid/hooks' +import { useFeatureFlag } from '@easypid/hooks/useFeatureFlag' import { useHaptics } from '@package/app/src/hooks' import { Platform } from 'react-native' import { FadeIn } from 'react-native-reanimated' @@ -31,6 +33,7 @@ export function FunkeWalletScreen() { const toast = useToastController() const { userName, isLoading } = useFirstNameFromPidCredential() + const hasEidCardFeatureFlag = useFeatureFlag('EID_CARD') const pushToMenu = withHaptics(() => push('/menu')) const pushToScanner = withHaptics(() => push('/scan')) @@ -86,17 +89,22 @@ export function FunkeWalletScreen() { onPress={pushToOffline} /> - - {userName ? ( - - How does it work? - - ) : ( - - Setup your ID - - )} - + + {hasEidCardFeatureFlag ? ( + + {userName ? ( + + How does it work? + + ) : ( + + Setup your ID + + )} + + ) : ( + + )} diff --git a/apps/easypid/src/hooks/useFeatureFlag.tsx b/apps/easypid/src/hooks/useFeatureFlag.tsx index e7009d5c..678a5513 100644 --- a/apps/easypid/src/hooks/useFeatureFlag.tsx +++ b/apps/easypid/src/hooks/useFeatureFlag.tsx @@ -5,3 +5,11 @@ import type { FeatureKey } from '../config/features' export const useFeatureFlag = (featureKey: FeatureKey) => { return APP_CONFIGS[CURRENT_APP_TYPE]?.[featureKey] ?? false } + +export const useIsFunkeWallet = () => { + return CURRENT_APP_TYPE === 'FUNKE_WALLET' +} + +export const useIsParadymWallet = () => { + return CURRENT_APP_TYPE === 'PARADYM_WALLET' +} diff --git a/packages/ui/src/components/PinPad.tsx b/packages/ui/src/components/PinPad.tsx index 0ba431c2..690dc0d2 100644 --- a/packages/ui/src/components/PinPad.tsx +++ b/packages/ui/src/components/PinPad.tsx @@ -108,6 +108,7 @@ export const PinPad = ({ onPressPinNumber, useBiometricsPad, disabled, biometric // biome-ignore lint/suspicious/noArrayIndexKey: key={rowIndex} borderTopWidth="$0.5" + borderBottomWidth={rowIndex === pinValues.length - 1 ? '$0.5' : 0} bc="$grey-200" borderColor="$grey-200" w="100%"