From 2cfd99d614471d4fa75c8c2787d269af16f6746c Mon Sep 17 00:00:00 2001 From: Alka Prasad Date: Mon, 1 Apr 2024 11:24:33 +0530 Subject: [PATCH] [INJIMOB-878] : vc state machine refactoring (#1344) * [INJI-631] add share and share with selfie options into kebab menu Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJI-631] change share vc component service attribute type to existingMosipVcItemMachine Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJI-631] change the kebab menu options name as per the wireframe Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJI-631] add missing otp screen confirmation dialog translations for kannada language Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJI-631] extract vcItemField name and value into two separate components for better usability Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJI-631] show mini card view of the loaded VC and change the styles as per the wireframe Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJI-631] do not show the activation status and kebab menu when sharing vc and while doing QR login Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJI-631] show mini card view usin shimmerPlaceholder component when card is in loading state Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJI-631] add svg images for wallet activated and unactivated icon Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJI-631] fix the postion of pin icon when there is no image displayed in VC Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] show help icon and meat ball menu in vc detailed view Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] add styles for detailed view profile icon and it's container Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-721]: add help icon on top bar and remove setting button Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> * [INJIMOB-721]: move settings to bottom tab bar Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> * [INJIMOB-721]: add search bar ui for vc search Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> * [INJIMOB-721]: add search filter for the vc search Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> * [INJIMOB-721]: refactor filtered data to show message when vc not found Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> * [INJIMOB-631] redirect to the home screen when user gives confirmation to delete the vc from the detailed view Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-721]: add search svg for search bar and also style it Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> * [INJIMOB-631] in existing vcitem machine close the kebab popup when an option is selected in kebab menu except for remove vc option Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] change the remove vc confirm popup as per the wireframe Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] add icons for the kebab menu options and show the number of cards available in home screen Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] remove tooltip in detailed view and show remove vc kebab menu option in red color Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] close the kebab menu when user clicks on cancel button in removal confirmation popup Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMON-631] show verification status for all the VCs in mini view and detailed view based on isVerified status Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631]: send vc data to scan machine while selecting share or share with selfie from meatball menu Signed-off-by: Alka Prasad * [INJIMOB-721]: add styles to search bar Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> * [INJIMOB-721]: add text msg when field searched in the vc search bar is not found Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> * [INJIMOB-631]: implement sharing VC flow from mini card view Signed-off-by: Alka Prasad * [INJIMOB-721]: refactor search bar component to show diff icon for vc search and issuers search Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> * [INJIMOB-721]: remove unused code Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> * [INJIMOB-631]: implement sharing VC with selfie flow from mini card view Signed-off-by: Alka Prasad * [INJIMOB-631]: handle face auth failure scenario in VC sharing with selfie Signed-off-by: Alka Prasad * [INJIMOB-631]: handle proper closing of kebab menu on VC share option click Signed-off-by: Alka Prasad * [INJIMOB-721]: add styles to the search bar Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> * [INJIMOB-631]: handle navigation to history page from success vc share page Signed-off-by: Alka Prasad * [INJIMOB-721]: add background colour for home screen Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> * [INJIMOB-631]: display proper label for QR login from meat ball menu Signed-off-by: Alka Prasad * [INJIMOB-631]: handle QR login flow for mini view card Signed-off-by: Alka Prasad * [INJIMOB-631] show count of cards available matching with search keyword Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] show activated icon for the sunbird cards in the mini view Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] hide share with selfie and qr login options in kebab menu if profile image is not available Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631]: updating logic to call isFromOpenId4VCI from an instance of VCMetadata Signed-off-by: Alka Prasad * [INJIMOB-631]: update kebab menu list icons to outline with black color Signed-off-by: Alka Prasad * [INJIMOB-631]: close kebab popup on selecting sharing option for esignet VCs Signed-off-by: Alka Prasad * [INJIMOB-631]: clear connect for invalid identity and back button click face auth during share with selfie Signed-off-by: Alka Prasad * [INJIMOB-631] fix the logic of showing how many cards available in the ui Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] remove styles for kebab menu pin icon Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631]: updating logic to call isFromOpenId4VCI from an instance of VCMetadata Signed-off-by: Alka Prasad * [INJIMOB-631]: reset the flowType and selectedVC variable in scan machine on any disconnect or success share of VC Signed-off-by: Alka Prasad * [INJIMOB-631] in detail view show activation status popup everytime in the bottom Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] replace activated icon with svg image in detailed view Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631]: remove unused files Signed-off-by: Alka Prasad * [INJIMOB-631] don't show activation details in detailed view if VC image is not available Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] show verification status as valid if credentials are available Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] adjust the size of the image, qr code, magnifier and logo in detialed view Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] increase the width of the activate button in the detialed view Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] in detialed view increase the divider with and change it's styles to show it as per wireframe Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] in detialed view move email and all the address fields to the bottom view for all type of VCs Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] filter the supported credentials of esignet wellknown config based on credential type of issuers config Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] show the horizontal line in detailed view only if email or one of the address fields available in vc Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] fix the spelling mistake of detailed view image styles Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] remove unused event in existing mosip vc item machine Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] change the name of the enum used for sharing flow from flow type to vc share flow type Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] create a new enum type to use it for showing the activation status in vcitem container Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] use logical and operator to decide whether to show horizontal line or not in detailed view and remove unnecessary logs Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] add test id to the profile icon container and adjust the styles of pin icon Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] move invalid identity message overlay to verify identity overlay component Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] show credential registry in the bottom section of detial view if it's env variable is set to true Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] add testId to detailed view qr code view and change settings tab title to settings Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] make whole kebab menu option as pressable Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631]: hide kebab menu from the VC on SendVCScreen Signed-off-by: Alka Prasad * [INJIMOB-631]: reset SelectedVc and flow type in scan machine for every kind of disconnect Signed-off-by: Alka Prasad * [INJIMOB-631]: remove redundant check on qr login Signed-off-by: Alka Prasad * [INJIMOB-631]: fix the prop value passed to the face scanner Signed-off-by: Alka Prasad * [INJIMOB-631] add email & address back to default fields and write a logic to remove bottom section fields from wellknown config supported fields Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] move detailed view bottom section fields list into vcUtils file Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] remove unused action selectIsVerifiable in existing vc item machine Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] pass the controller functions directly to verifyIdentityOverlay when calling it Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] add todo comment in vcVerification file to handled vc verification pending status as part of another task Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-631] render kebab menu options using flat list and make necessary changes related to menu options Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-721]: refactor search Filter for normal otp flow VC Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> * [INJIMOB-721]: refactor search Filter for sunbird and esignet vc Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> * [INJIMOB-721]: refactor to add metadata to esignet and sunbird vc on download before saving Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> * [INJIMOB-721]: show no of cards when search data is updated Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> * [INJIMOB-631] add back history tab file Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * INJIMOB-778 (#122) * [INJIMOB-778]: add new localistaion for set and confirm passcode screen Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> * [INJIMOB-778]: add authorization after the biometric unlock Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> * [INJIMOB-778]: add check for biometric and password for the toggle Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> * [INJIMOB-778]: add success toaster message when alternate unlock method is added Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> * [INJIMOB-778]: remove toaster when the app is reopened Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> * [INJIMOB-778]: add locales for succes toaster for biometric toggle Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> * [INJIMOB-778]: refactor to check for the toggle from settings Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> * [INJIMOB-778]: refactor function that handles biometric toggle Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> * [INJIMOB-778]: refactor toggle_biometric_unlock event Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> * [INJIMOB-778]: refactor banner notification to show success message Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> * [INJIMOB-778]: add testID for new components Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> --------- Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> Signed-off-by: PoojaBabusing <115976560+PoojaBabusing@users.noreply.github.com> Co-authored-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> * Injimob 784 (#124) * [INJIMOB-784] add face verification consent overlay Signed-off-by: adityankannan-tw * [INJIMOB-784] add dismiss for backdrop press Signed-off-by: adityankannan-tw * [INJIMOB-784] add pop up for qrlogin flow also Signed-off-by: adityankannan-tw * [INJIMOB-864] disable backup button when restoring and vice versa (#1279) Signed-off-by: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com> * [INJIMOB-867] - remove all files including unsynced files and get latest file based on date (#1281) Signed-off-by: Sreenadh S <32409698+sree96@users.noreply.github.com> * [INJIMOB - 865,868] - Fix duplicate VC key for restored VC's and remove metadata if vc file not found (#1283) * [INJIMOB-something] use incremental timestamp for restore and make unload take a flag start time Signed-off-by: Harsh Vardhan * [INJIMOB-865,868] fix duplicate vckey value for restored vcs and remove vc metadata if file not found Co-authored-by: adityankannan-tw <109274996+adityankannan-tw@users.noreply.github.com> Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-869] remove unused method in store.ts machine Co-authored-by: adityankannan-tw <109274996+adityankannan-tw@users.noreply.github.com> Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-869] revert removing vc metadata if file not found Co-authored-by: adityankannan-tw <109274996+adityankannan-tw@users.noreply.github.com> Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-865] fix tamper vc removal When a VC is tampered, we get the tampered vc popup. Once we press okay button in the popup, we will be refreshing the vc machine's myVcs & recievedVcs context. Due to calling of parallel states, we were having issue with refreshing of myVcs. It is fixed by targeting to init state to refresh Co-authored-by: adityankannan-tw <109274996+adityankannan-tw@users.noreply.github.com> Co-authored-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> Signed-off-by: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com> * [INJIMOB-865] remove vc metadata when file is not found Co-authored-by: adityankannan-tw <109274996+adityankannan-tw@users.noreply.github.com> Co-authored-by: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com> Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-869] add code comments & fix minor code issues Signed-off-by: Harsh Vardhan --------- Signed-off-by: Harsh Vardhan Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> Signed-off-by: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com> Co-authored-by: Harsh Vardhan Co-authored-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> Co-authored-by: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com> * [INJIMOB-784] set context after getting the value from store Signed-off-by: adityankannan-tw * [INJIMOB-784] refactor the names and send isConsentGiven value properly to the scan machine event Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-784] store & fetch the face auth consent value with proper types Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * INJIMOB-604 VC Verification Failure Scenarios (#1228) * [INJI-604]: Handle Verification VC Error Scenarios Signed-off-by: BalachandarG * [INJIMOB-604] remove the card from UI when verify credential is failed and show error overlay Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-604] remove unused actions in existingMosipVcItem and issuers machine Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * INJIMOB-604 Replace Error message overlay with new error screen. Signed-off-by: BalachandarG * [INJI-611]: New Error screen implemented Signed-off-by: BalachandarG * [INJI-604]: Transation added for Verification error messages. Signed-off-by: BalachandarG * [INJI-604] merge develop and resolve conflicts Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJI-604] remove unnecessary log in error screen Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-604] remove removeVcFromMyVcs action from verify vc failed event and fix typegen warnings Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * INJIMOB-604 : Removing logs and renaming functions Signed-off-by: BalachandarG * [INJIMOB-604]: Removing unused method. Signed-off-by: BalachandarG * [Inji-604]: Fixing the issue of recursive VC deletion during Go Back event in Verification Error screen. Signed-off-by: BalachandarG * [Inji-604]: Fixing bugs post conflict resolve. Signed-off-by: BalachandarG * [Inji-604]: Removing unused events and adding translations for Go Back button * [Inji-604]: Removed angle brackets for Technical Error * [Inji-604]: Added ToDo to handle backup * [Inji-604]: Removing isVerified flag from the Existing VC Item Machine. * [Inji-604] iOS crash fixed for Verification error Go Back Button Signed-off-by: BalachandarG * [Inji-604]: Simplified condition for result in verify Credential call. Signed-off-by: BalachandarG --------- Signed-off-by: BalachandarG Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> Co-authored-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> * [INJIMOB-784] add styles for the popup Signed-off-by: adityankannan-tw * [INJIMOB-784] add locales changes in all languages Signed-off-by: adityankannan-tw * [INJIMOB-784] send consent data using event to qrlogin machine Signed-off-by: adityankannan-tw * [INJIMOB-784] refactor scan and qr machines and remove logs Signed-off-by: adityankannan-tw * [INJIMOB-784] add testID for icons and buttons Signed-off-by: adityankannan-tw * [INJIMOB-784] add translations for help screen contents Signed-off-by: adityankannan-tw * [INJIMOB-784] add translations for help screen contents Signed-off-by: adityankannan-tw * [INJIMOB-784] change popup text and alter help screen msg order Signed-off-by: adityankannan-tw * Update SendVcScreenController.ts * [INJIMOB-784] refactor actions for show face auth consent screen Signed-off-by: adityankannan-tw --------- Signed-off-by: adityankannan-tw Signed-off-by: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com> Signed-off-by: Sreenadh S <32409698+sree96@users.noreply.github.com> Signed-off-by: Harsh Vardhan Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> Signed-off-by: BalachandarG Signed-off-by: adityankannan-tw <109274996+adityankannan-tw@users.noreply.github.com> Co-authored-by: adityankannan-tw Co-authored-by: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com> Co-authored-by: Sreenadh S <32409698+sree96@users.noreply.github.com> Co-authored-by: Harsh Vardhan Co-authored-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> Co-authored-by: balachandarg-tw <115633327+balachandarg-tw@users.noreply.github.com> * [INJIMOB-878]: VC State Machine Refactoring Signed-off-by: Vijay <94220135+vijay151096@users.noreply.github.com> * [INJIMOB-878]: removed unused component from the codebase Signed-off-by: Alka Prasad * [INJIMOB-878]: remove any traces of existing and esignet item state machine from the code base Signed-off-by: Alka Prasad * [INJIMOB-878]: extract out phone number and email id to a wrapper object Signed-off-by: Alka Prasad * [INJIMOB-878]: optimize context variable declaration and usage in vc and vcItemStateMachine Signed-off-by: Alka Prasad * [INJIMOB-878]: move all the selectors to VCItemMachine from a seperate file Signed-off-by: Alka Prasad * [INJIMOB-878]: bring back code lost in code merge from develop Signed-off-by: Alka Prasad * [INJIMOB-878]: remove unused imports and refactor the openId4VCI check from entire code base to a single point Signed-off-by: Alka Prasad * [INJIMOB-878]: remove the entire vc or context sharing from the state machine to the component Signed-off-by: Alka Prasad * [INJIMOB-878]: refactor controller to remove seperate declaration of variables Signed-off-by: Alka Prasad * [INJIMOB-878]: extract events of VCItemMAchine into a seperate file Signed-off-by: Alka Prasad * [INJIMOB-878]: removed some unused imports renamed VCItemSelectors file and refactored KebabPopUpController Signed-off-by: Alka Prasad * [INJIMOB-878]: add issuer in OTP flow VC data as well Signed-off-by: Alka Prasad * [INJIMOB-878]: remove VP implemetation and some unused components Signed-off-by: Alka Prasad * [INJIMOB-878]: split VCItemMachine into multiple logical units Signed-off-by: Alka Prasad * [INJIMOB-878]: correct a test id for activation flow Signed-off-by: Alka Prasad * [INJIMOB-878]: fix issues dueto merge conflict Signed-off-by: Alka Prasad * [INJIMOB-878]: changed the folder structure for VCItemMAchine and related files Signed-off-by: Alka Prasad --------- Signed-off-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> Signed-off-by: Alka Signed-off-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> Signed-off-by: Alka Prasad Signed-off-by: Alka Prasad Signed-off-by: PoojaBabusing <115976560+PoojaBabusing@users.noreply.github.com> Signed-off-by: adityankannan-tw Signed-off-by: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com> Signed-off-by: Sreenadh S <32409698+sree96@users.noreply.github.com> Signed-off-by: Harsh Vardhan Signed-off-by: BalachandarG Signed-off-by: adityankannan-tw <109274996+adityankannan-tw@users.noreply.github.com> Signed-off-by: Vijay <94220135+vijay151096@users.noreply.github.com> Co-authored-by: PuBHARGAVI <46226958+PuBHARGAVI@users.noreply.github.com> Co-authored-by: Pooja Babusingh <68894211+PoojaBabusingh@users.noreply.github.com> Co-authored-by: PoojaBabusing <115976560+PoojaBabusing@users.noreply.github.com> Co-authored-by: adityankannan-tw <109274996+adityankannan-tw@users.noreply.github.com> Co-authored-by: adityankannan-tw Co-authored-by: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com> Co-authored-by: Sreenadh S <32409698+sree96@users.noreply.github.com> Co-authored-by: Harsh Vardhan Co-authored-by: balachandarg-tw <115633327+balachandarg-tw@users.noreply.github.com> Co-authored-by: Vijay <94220135+vijay151096@users.noreply.github.com> --- .env | 4 +- .talismanrc | 22 +- README.md | 5 +- components/KebabPopUp.tsx | 4 +- components/KebabPopUpController.tsx | 152 +- components/VC/VCItemController.tsx | 45 + components/VC/VcDetailsContainer.tsx | 10 +- components/VC/VcItemContainer.tsx | 10 +- components/VC/VcItemController.tsx | 67 - components/VC/Views/VCCardView.tsx | 72 +- components/VC/Views/VCCardViewContent.tsx | 81 +- components/VC/Views/VCDetailView.tsx | 137 +- components/VC/common/VCCardSkeleton.tsx | 70 +- components/VC/common/VCUtils.tsx | 27 +- components/VcItemActivationStatus.tsx | 135 -- components/VcItemContainerProfileImage.tsx | 25 +- components/VcItemContent.tsx | 205 --- components/VidItem.tsx | 110 -- components/WalletBindingSuccessController.tsx | 2 +- components/kebabMenuUtils.ts | 8 +- components/ui/Modal.tsx | 4 +- components/ui/svg.tsx | 3 + components/ui/themes/DefaultTheme.ts | 25 - components/ui/themes/PurpleTheme.ts | 25 - i18n.ts | 3 +- machines/QrLoginMachine.ts | 37 +- machines/QrLoginMachine.typegen.ts | 8 +- .../EsignetMosipVCItemMachine.ts | 870 ----------- .../EsignetMosipVCItemMachine.typegen.ts | 184 --- .../ExistingMosipVCItemMachine.ts | 1312 ----------------- .../ExistingMosipVCItemMachine.typegen.ts | 301 ---- machines/VCItemMachine/commonSelectors.ts | 91 -- .../VCItemMachine/VCItemActions.ts | 431 ++++++ .../VCItemMachine/VCItemEvents.ts | 27 + .../VCItemMachine/VCItemGaurds.ts | 19 + .../VCItemMachine/VCItemMachine.ts | 536 +++++++ .../VCItemMachine/VCItemMachine.typegen.ts | 378 +++++ .../VCItemMachine/VCItemModel.ts | 34 + .../VCItemMachine/VCItemSelectors.ts | 114 ++ .../VCItemMachine/VCItemServices.ts | 208 +++ .../VCMetaMachine}/vc.ts | 72 +- .../VCMetaMachine}/vc.typegen.ts | 7 +- machines/app.ts | 5 +- machines/auth.typegen.ts | 3 +- machines/backupAndRestore/backupRestore.ts | 2 +- machines/bleShare/request/requestMachine.ts | 72 +- .../request/requestMachine.typegen.ts | 29 +- machines/bleShare/request/selectors.ts | 33 +- machines/bleShare/scan/scanMachine.ts | 101 +- machines/bleShare/scan/scanMachine.typegen.ts | 46 +- machines/bleShare/scan/selectors.ts | 29 +- machines/issuersMachine.ts | 7 +- machines/issuersMachine.typegen.ts | 2 + machines/settings.ts | 10 +- machines/settings.typegen.ts | 2 + screens/Home/HomeScreen.tsx | 9 +- screens/Home/HomeScreenController.ts | 8 +- screens/Home/HomeScreenMachine.ts | 13 +- screens/Home/MyVcs/AddVcModalMachine.ts | 2 +- screens/Home/MyVcs/HistoryTab.tsx | 4 +- screens/Home/MyVcs/IdInputModalController.ts | 10 +- .../MyVcs/OtpVerificationModalController.ts | 3 +- screens/Home/MyVcs/RemoveVcWarningOverlay.tsx | 4 +- screens/Home/MyVcs/WalletBinding.tsx | 41 +- screens/Home/MyVcsTab.tsx | 12 +- screens/Home/MyVcsTabController.ts | 57 +- screens/Home/MyVcsTabMachine.ts | 11 +- screens/Home/ReceivedVcsTabController.ts | 13 +- screens/Home/ReceivedVcsTabMachine.ts | 6 +- screens/Home/ViewVcModal.tsx | 71 +- screens/Home/ViewVcModalController.ts | 77 +- screens/QrLogin/QrLogin.tsx | 8 +- screens/QrLogin/QrLoginController.ts | 72 +- screens/Request/ReceiveVcScreen.tsx | 13 +- screens/Request/ReceiveVcScreenController.ts | 14 +- screens/Request/RequestScreenController.ts | 3 - screens/Scan/ScanLayout.tsx | 3 +- screens/Scan/ScanLayoutController.ts | 14 +- screens/Scan/ScanScreenController.ts | 2 +- screens/Scan/SelectVcOverlayController.ts | 8 +- screens/Scan/SendVcScreen.tsx | 48 +- screens/Scan/SendVcScreenController.ts | 43 +- screens/VerifyIdentityOverlay.tsx | 18 +- shared/GlobalContext.ts | 2 +- shared/Utils.ts | 5 + shared/VCMetadata.ts | 2 +- shared/commonUtil.ts | 2 +- shared/constants.ts | 2 +- shared/cryptoutil/cryptoUtil.ts | 7 - shared/openId4VCI/Utils.ts | 3 +- shared/request.ts | 5 +- shared/vcjs/createVerifiablePresentation.ts | 5 +- shared/vcjs/verifyCredential.ts | 9 +- shared/vcjs/verifyPresentation.ts | 2 +- types/VC/ExistingMosipVC/vc.ts | 111 -- types/VC/{EsignetMosipVC/vc.ts => vc.d.ts} | 31 +- 96 files changed, 2432 insertions(+), 4562 deletions(-) create mode 100644 components/VC/VCItemController.tsx delete mode 100644 components/VC/VcItemController.tsx delete mode 100644 components/VcItemActivationStatus.tsx delete mode 100644 components/VcItemContent.tsx delete mode 100644 components/VidItem.tsx delete mode 100644 machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine.ts delete mode 100644 machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine.typegen.ts delete mode 100644 machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine.ts delete mode 100644 machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine.typegen.ts delete mode 100644 machines/VCItemMachine/commonSelectors.ts create mode 100644 machines/VerifiableCredential/VCItemMachine/VCItemActions.ts create mode 100644 machines/VerifiableCredential/VCItemMachine/VCItemEvents.ts create mode 100644 machines/VerifiableCredential/VCItemMachine/VCItemGaurds.ts create mode 100644 machines/VerifiableCredential/VCItemMachine/VCItemMachine.ts create mode 100644 machines/VerifiableCredential/VCItemMachine/VCItemMachine.typegen.ts create mode 100644 machines/VerifiableCredential/VCItemMachine/VCItemModel.ts create mode 100644 machines/VerifiableCredential/VCItemMachine/VCItemSelectors.ts create mode 100644 machines/VerifiableCredential/VCItemMachine/VCItemServices.ts rename machines/{VCItemMachine => VerifiableCredential/VCMetaMachine}/vc.ts (88%) rename machines/{VCItemMachine => VerifiableCredential/VCMetaMachine}/vc.typegen.ts (94%) delete mode 100644 types/VC/ExistingMosipVC/vc.ts rename types/VC/{EsignetMosipVC/vc.ts => vc.d.ts} (74%) diff --git a/.env b/.env index 5feeba55..c6eb448f 100644 --- a/.env +++ b/.env @@ -2,9 +2,9 @@ # eg . npm build android:newlogic --reset-cache #MIMOTO_HOST=http://mock.mimoto.newlogic.dev -MIMOTO_HOST=https://api.qa-inji1.mosip.net +MIMOTO_HOST=https://api.qa-inji.mosip.net -ESIGNET_HOST=https://api.qa-inji1.mosip.net +ESIGNET_HOST=https://api.qa-inji.mosip.net OBSRV_HOST = https://dataset-api.obsrv.mosip.net diff --git a/.talismanrc b/.talismanrc index a01a6295..cdb96c4d 100644 --- a/.talismanrc +++ b/.talismanrc @@ -47,14 +47,6 @@ fileignoreconfig: checksum: 6d22bc5c77398316b943c512c208ce0846a9fff674c1ccac79e07f21962acd5f - filename: shared/telemetry/TelemetryConstants.js checksum: fd8dc3a69cdef68855dc5f0531d8e634bfa2621bb4dc22f85b8247512a349c4c - - filename: machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine.ts - checksum: cca7657da3a1a91b63e0a10605545d1e0d3d152b083a55f9dbb9eed893646e4b - - filename: machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine.ts - checksum: 1d06c737c0a59cdda6604eb2a6f384bcaf361a682c19f43ef9e8537e38c53db4 - - filename: machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine.typegen.ts - checksum: 0de47337ba60b29c8e4d4231439c77028197d3d8d3139eb367c471138c40f93d - - filename: machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine.typegen.ts - checksum: 86ecb95b58456d42676247eb768a7fba980a3c0ef83d515f8b9ce0c44d8c6906 - filename: shared/telemetry/TelemetryUtils.js checksum: a0686e9a5c006176fd720b84bc36ca947db1422caf65c90cd0c2d1102a9e96df - filename: ios/fastlane/Fastfile @@ -115,8 +107,6 @@ fileignoreconfig: checksum: ac9c154060c7c1adb3392ac8c78a42cae5ca3faea3b4b0166dd00d4ca38b290d - filename: machines/settings.typegen.ts checksum: e4ae05822f1b1c23f3f70d03dd46fd8f29ba6b52d40f2f24c121f536fbb5f2c4 - - filename: .env - checksum: 387e1fbafb92f58b152b751ec97e8e0e31a699c43e273c61bdedb67f8ee7e453 - filename: .github/workflows/ui-automation.yml checksum: 0b26a5dcb7524ba15d6aaeaf04f2ef94be9d25ef702d9072d6628bcd58e50f36 - filename: injitest/src/test/java/androidTestCases/PinVcTest.java @@ -211,5 +201,17 @@ fileignoreconfig: checksum: 1a15e4327b358ffee8bddfe412a38873ad6d4414d2b1d6bffd3c1782f5ec852f - filename: screens/Home/IntroSlidersScreen.tsx checksum: 9880724461b194db7651737576ad2fd2db9cf3b4e732747f59be422a7ff4e4a1 + - filename: .env + checksum: 9386b7d2d9bba27d8f15e6cb482451dc45b785b4caa31bc29718ef16967e6ed1 + - filename: machines/VCItemMachine/VCItemMachine.typegen.ts + checksum: 850b5d02636bef9e286fc0fbc4ffffbd38068f332c319302a906496f4bc1c8a1 + - filename: machines/VCItemMachine/VCItemEvents.ts + checksum: 04e5758d4fa8bc37e8b66f7f51627a9e71ccbca7a046aa64e914f5cf855aa48b + - filename: machines/VCItemMachine/VCItemGaurds.ts + checksum: 4f32814fc26a0edaa54a42dbc9f9e1d899144eb059ac8da211d1738887871829 + - filename: machines/VCItemMachine/VCItemServices.ts + checksum: 51b4872a64abd76b124000358068c0b213d50fb131d735c122cd9a177cd8390c + - filename: machines/VCItemMachine/VCItemActions.ts + checksum: cda2ec61f0b884e537d05d018330d7b3c6febbf10ad4cd8cd87c715e6ad1dcf4 version: "" diff --git a/README.md b/README.md index 0ecbfa93..058c9a86 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,11 @@ Inji Mobile Wallet is a mobile application specifically created to streamline all types of identification and credentials into one digital wallet. It offers a secure, trustworthy, and dependable mobile Verifiable Credentials wallet designed to fulfil the following functions + - Download and store Verifiable Credentials - Conduct offline face authentication -- Share Verifiable Credentials -- Enable users to log in to online portals +- Share Verifiable Credentials +- Enable users to log in to online portals for more details refer [here](https://docs.mosip.io/inji) diff --git a/components/KebabPopUp.tsx b/components/KebabPopUp.tsx index 3cf2e89e..6430b2f5 100644 --- a/components/KebabPopUp.tsx +++ b/components/KebabPopUp.tsx @@ -5,12 +5,12 @@ import {Column, Row, Text} from '../components/ui'; import {View} from 'react-native'; import {useKebabPopUp} from './KebabPopUpController'; import {ActorRefFrom} from 'xstate'; -import {ExistingMosipVCItemMachine} from '../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine'; import {useTranslation} from 'react-i18next'; import {FlatList} from 'react-native-gesture-handler'; import {VCMetadata} from '../shared/VCMetadata'; import testIDProps from '../shared/commonUtil'; import {getKebabMenuOptions} from './kebabMenuUtils'; +import {VCItemMachine} from '../machines/VerifiableCredential/VCItemMachine/VCItemMachine'; export const KebabPopUp: React.FC = props => { const controller = useKebabPopUp(props); @@ -83,7 +83,7 @@ export interface KebabPopUpProps { vcMetadata: VCMetadata; isVisible?: boolean; onDismiss: () => void; - service: ActorRefFrom; + service: ActorRefFrom; iconColor?: any; icon?: any; vcHasImage: boolean; diff --git a/components/KebabPopUpController.tsx b/components/KebabPopUpController.tsx index d4458e42..6ddde3ad 100644 --- a/components/KebabPopUpController.tsx +++ b/components/KebabPopUpController.tsx @@ -1,134 +1,86 @@ import {useSelector} from '@xstate/react'; import {ActorRefFrom} from 'xstate'; import { + selectAcceptingBindingOtp, selectBindingAuthFailedError, - selectEmptyWalletBindingId, + selectBindingWarning, + selectIsCommunicationDetails, selectIsPinned, selectKebabPopUp, - selectAcceptingBindingOtp, - selectBindingWarning, - selectWalletBindingInProgress, - selectOtpError, selectRemoveWalletWarning, selectShowActivities, + selectWalletBindingResponse, selectShowWalletBindingError, - selectWalletBindingError, - selectIsPhoneNumber, - selectIsEmail, -} from '../machines/VCItemMachine/commonSelectors'; -import { - ExistingMosipVCItemEvents, - ExistingMosipVCItemMachine, -} from '../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine'; -import { - EsignetMosipVCItemEvents, - EsignetMosipVCItemMachine, -} from '../machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine'; + selectWalletBindingInProgress, +} from '../machines/VerifiableCredential/VCItemMachine/VCItemSelectors'; import {selectActivities} from '../machines/activityLog'; import {GlobalContext} from '../shared/GlobalContext'; -import {useContext, useState} from 'react'; +import {useContext} from 'react'; import {VCMetadata} from '../shared/VCMetadata'; import {ScanEvents} from '../machines/bleShare/scan/scanMachine'; -import { - BOTTOM_TAB_ROUTES, - SCAN_ROUTES, - ScanStackParamList, -} from '../routes/routesConstants'; +import {BOTTOM_TAB_ROUTES, ScanStackParamList} from '../routes/routesConstants'; import {NavigationProp, useNavigation} from '@react-navigation/native'; import {MainBottomTabParamList} from '../routes/main'; import {selectIsScanning} from '../machines/bleShare/scan/selectors'; +import { + VCItemEvents, + VCItemMachine, +} from '../machines/VerifiableCredential/VCItemMachine/VCItemMachine'; +import {selectError} from '../machines/biometrics'; type ScanLayoutNavigation = NavigationProp< ScanStackParamList & MainBottomTabParamList >; export function useKebabPopUp(props) { - const service = props.service as - | ActorRefFrom - | ActorRefFrom; + const service = props.service as ActorRefFrom; const navigation = useNavigation(); - const vcEvents = - props.vcKey !== undefined && props.vcMetadata.isFromOpenId4VCI() - ? EsignetMosipVCItemEvents - : ExistingMosipVCItemEvents; - const PIN_CARD = () => service.send(vcEvents.PIN_CARD()); - const KEBAB_POPUP = () => service.send(vcEvents.KEBAB_POPUP()); - const ADD_WALLET_BINDING_ID = () => - service.send(vcEvents.ADD_WALLET_BINDING_ID()); - const CONFIRM = () => service.send(vcEvents.CONFIRM()); - const REMOVE = (vcMetadata: VCMetadata) => - service.send(vcEvents.REMOVE(vcMetadata)); - const DISMISS = () => service.send(vcEvents.DISMISS()); - const CANCEL = () => service.send(vcEvents.CANCEL()); - const SHOW_ACTIVITY = () => service.send(vcEvents.SHOW_ACTIVITY()); - const INPUT_OTP = (otp: string) => service.send(vcEvents.INPUT_OTP(otp)); - const RESEND_OTP = () => service.send(vcEvents.RESEND_OTP()); - const isPinned = useSelector(service, selectIsPinned); - const isBindingWarning = useSelector(service, selectBindingWarning); - const isRemoveWalletWarning = useSelector(service, selectRemoveWalletWarning); - const isAcceptingOtpInput = useSelector(service, selectAcceptingBindingOtp); - const isWalletBindingError = useSelector( - service, - selectShowWalletBindingError, - ); - const otpError = useSelector(service, selectOtpError); - const walletBindingError = useSelector(service, selectWalletBindingError); - const bindingAuthFailedError = useSelector( - service, - selectBindingAuthFailedError, - ); - const WalletBindingInProgress = useSelector( - service, - selectWalletBindingInProgress, - ); - const emptyWalletBindingId = useSelector(service, selectEmptyWalletBindingId); - const isKebabPopUp = useSelector(service, selectKebabPopUp); - const isShowActivities = useSelector(service, selectShowActivities); - const phoneNumber = useSelector(service, selectIsPhoneNumber); - const email = useSelector(service, selectIsEmail); const {appService} = useContext(GlobalContext); - const activityLogService = appService.children.get('activityLog'); - const scanService = appService.children.get('scan'); - const isScanning = useSelector(scanService, selectIsScanning); - - const GOTO_SCANSCREEN = () => { - navigation.navigate(BOTTOM_TAB_ROUTES.share); - }; + const activityLogService = appService.children.get('activityLog')!!; + const scanService = appService.children.get('scan')!!; return { - isPinned, - PIN_CARD, - KEBAB_POPUP, - ADD_WALLET_BINDING_ID, - CONFIRM, - GOTO_SCANSCREEN, - DISMISS, - REMOVE, - CANCEL, - INPUT_OTP, - RESEND_OTP, - SHOW_ACTIVITY, + service: props.service as ActorRefFrom, + navigation: useNavigation(), + isScanning: useSelector(scanService, selectIsScanning), + activities: useSelector(activityLogService, selectActivities), + isPinned: useSelector(service, selectIsPinned), + isBindingWarning: useSelector(service, selectBindingWarning), + isRemoveWalletWarning: useSelector(service, selectRemoveWalletWarning), + isAcceptingOtpInput: useSelector(service, selectAcceptingBindingOtp), + isWalletBindingError: useSelector(service, selectShowWalletBindingError), + walletBindingResponse: useSelector(service, selectWalletBindingResponse), + otpError: useSelector(service, selectError), + walletBindingError: useSelector(service, selectError), + bindingAuthFailedError: useSelector(service, selectBindingAuthFailedError), + isKebabPopUp: useSelector(service, selectKebabPopUp), + isShowActivities: useSelector(service, selectShowActivities), + communicationDetails: useSelector(service, selectIsCommunicationDetails), + walletBindingInProgress: useSelector( + service, + selectWalletBindingInProgress, + ), + PIN_CARD: () => service.send(VCItemEvents.PIN_CARD()), + KEBAB_POPUP: () => service.send(VCItemEvents.KEBAB_POPUP()), + ADD_WALLET_BINDING_ID: () => + service.send(VCItemEvents.ADD_WALLET_BINDING_ID()), + CONFIRM: () => service.send(VCItemEvents.CONFIRM()), + REMOVE: (vcMetadata: VCMetadata) => + service.send(VCItemEvents.REMOVE(vcMetadata)), + DISMISS: () => service.send(VCItemEvents.DISMISS()), + CANCEL: () => service.send(VCItemEvents.CANCEL()), + SHOW_ACTIVITY: () => service.send(VCItemEvents.SHOW_ACTIVITY()), + INPUT_OTP: (otp: string) => service.send(VCItemEvents.INPUT_OTP(otp)), + RESEND_OTP: () => service.send(VCItemEvents.RESEND_OTP()), + GOTO_SCANSCREEN: () => { + navigation.navigate(BOTTOM_TAB_ROUTES.share); + }, SELECT_VC_ITEM: ( - vcRef: ActorRefFrom, + vcRef: ActorRefFrom, flowType: string, ) => { const {serviceRefs, ...vcData} = vcRef.getSnapshot().context; scanService.send(ScanEvents.SELECT_VC(vcData, flowType)); }, - isScanning, - isBindingWarning, - isAcceptingOtpInput, - isWalletBindingError, - walletBindingError, - bindingAuthFailedError, - otpError, - WalletBindingInProgress, - emptyWalletBindingId, - isKebabPopUp, - isShowActivities, - isRemoveWalletWarning, - activities: useSelector(activityLogService, selectActivities), - phoneNumber, - email, }; } diff --git a/components/VC/VCItemController.tsx b/components/VC/VCItemController.tsx new file mode 100644 index 00000000..ee89beea --- /dev/null +++ b/components/VC/VCItemController.tsx @@ -0,0 +1,45 @@ +import {useContext, useRef} from 'react'; +import {GlobalContext} from '../../shared/GlobalContext'; +import { + selectContext, + selectGeneratedOn, + selectKebabPopUp, + selectWalletBindingResponse, + selectCredential, + selectVerifiableCredentialData, +} from '../../machines/VerifiableCredential/VCItemMachine/VCItemSelectors'; +import {useInterpret, useSelector} from '@xstate/react'; +import {VCItemProps} from './Views/VCCardView'; +import { + createVCItemMachine, + VCItemEvents, +} from '../../machines/VerifiableCredential/VCItemMachine/VCItemMachine'; +import {selectIsSavingFailedInIdle} from '../../screens/Home/MyVcsTabMachine'; + +export function useVcItemController(props: VCItemProps) { + const {appService} = useContext(GlobalContext); + const machine = useRef( + createVCItemMachine( + appService.getSnapshot().context.serviceRefs, + props.vcMetadata, + ), + ); + const service = useInterpret(machine.current, {devTools: __DEV__}); + + return { + service, + context: useSelector(service, selectContext), + credential: useSelector(service, selectCredential), + verifiableCredentialData: useSelector( + service, + selectVerifiableCredentialData, + ), + walletBindingResponse: useSelector(service, selectWalletBindingResponse), + isKebabPopUp: useSelector(service, selectKebabPopUp), + DISMISS: () => service.send(VCItemEvents.DISMISS()), + KEBAB_POPUP: () => service.send(VCItemEvents.KEBAB_POPUP()), + isSavingFailedInIdle: useSelector(service, selectIsSavingFailedInIdle), + storeErrorTranslationPath: 'errors.savingFailed', + generatedOn: useSelector(service, selectGeneratedOn), + }; +} diff --git a/components/VC/VcDetailsContainer.tsx b/components/VC/VcDetailsContainer.tsx index 307a1615..dc57f643 100644 --- a/components/VC/VcDetailsContainer.tsx +++ b/components/VC/VcDetailsContainer.tsx @@ -1,12 +1,6 @@ import React from 'react'; -import { - EsignetVCItemDetailsProps, - ExistingVCItemDetailsProps, - VCDetailView, -} from './Views/VCDetailView'; +import {VCDetailView, VCItemDetailsProps} from './Views/VCDetailView'; -export const VcDetailsContainer: React.FC< - ExistingVCItemDetailsProps | EsignetVCItemDetailsProps -> = props => { +export const VcDetailsContainer: React.FC = props => { return ; }; diff --git a/components/VC/VcItemContainer.tsx b/components/VC/VcItemContainer.tsx index a6fd9d2c..4e0badc3 100644 --- a/components/VC/VcItemContainer.tsx +++ b/components/VC/VcItemContainer.tsx @@ -1,12 +1,6 @@ import React from 'react'; -import { - VCCardView, - EsignetVCItemProps, - ExistingVCItemProps, -} from './Views/VCCardView'; +import {VCCardView, VCItemProps} from './Views/VCCardView'; -export const VcItemContainer: React.FC< - ExistingVCItemProps | EsignetVCItemProps -> = props => { +export const VcItemContainer: React.FC = props => { return ; }; diff --git a/components/VC/VcItemController.tsx b/components/VC/VcItemController.tsx deleted file mode 100644 index a3805683..00000000 --- a/components/VC/VcItemController.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import {useContext, useRef} from 'react'; -import {GlobalContext} from '../../shared/GlobalContext'; -import { - selectContext, - selectEmptyWalletBindingId, - selectGeneratedOn, - selectKebabPopUp, - selectVerifiableCredential, -} from '../../machines/VCItemMachine/commonSelectors'; -import { - createExistingMosipVCItemMachine, - ExistingMosipVCItemEvents, - selectIsSavingFailedInIdle, -} from '../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine'; -import { - createEsignetMosipVCItemMachine, - EsignetMosipVCItemEvents, -} from '../../machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine'; -import {useInterpret, useSelector} from '@xstate/react'; -import {EsignetVCItemProps, ExistingVCItemProps} from './Views/VCCardView'; -import {VCMetadata} from '../../shared/VCMetadata'; - -export function useVcItemController( - props: ExistingVCItemProps | EsignetVCItemProps, -) { - const {appService} = useContext(GlobalContext); - const vcMetadata = VCMetadata.fromVC(props.vcMetadata); - const machine = useRef( - !vcMetadata.isFromOpenId4VCI() - ? createExistingMosipVCItemMachine( - appService.getSnapshot().context.serviceRefs, - props.vcMetadata, - ) - : createEsignetMosipVCItemMachine( - appService.getSnapshot().context.serviceRefs, - props.vcMetadata, - ), - ); - - const service = useInterpret(machine.current, {devTools: __DEV__}); - - const context = useSelector(service, selectContext); - const verifiableCredential = useSelector(service, selectVerifiableCredential); - const emptyWalletBindingId = useSelector(service, selectEmptyWalletBindingId); - const isKebabPopUp = useSelector(service, selectKebabPopUp); - let DISMISS = () => service.send(ExistingMosipVCItemEvents.DISMISS()); - let KEBAB_POPUP = () => service.send(ExistingMosipVCItemEvents.KEBAB_POPUP()); - const isSavingFailedInIdle = useSelector(service, selectIsSavingFailedInIdle); - const storeErrorTranslationPath = 'errors.savingFailed'; - const generatedOn = useSelector(service, selectGeneratedOn); - if (vcMetadata.isFromOpenId4VCI()) { - DISMISS = () => service.send(EsignetMosipVCItemEvents.DISMISS()); - KEBAB_POPUP = () => service.send(EsignetMosipVCItemEvents.KEBAB_POPUP()); - } - return { - service, - context, - verifiableCredential, - emptyWalletBindingId, - isKebabPopUp, - DISMISS, - KEBAB_POPUP, - isSavingFailedInIdle, - storeErrorTranslationPath, - generatedOn, - }; -} diff --git a/components/VC/Views/VCCardView.tsx b/components/VC/Views/VCCardView.tsx index 11cefdab..1d686de8 100644 --- a/components/VC/Views/VCCardView.tsx +++ b/components/VC/Views/VCCardView.tsx @@ -1,30 +1,28 @@ import React, {useEffect, useState} from 'react'; import {Pressable, View} from 'react-native'; import {ActorRefFrom} from 'xstate'; -import { - ExistingMosipVCItemEvents, - ExistingMosipVCItemMachine, -} from '../../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine'; import {ErrorMessageOverlay} from '../../MessageOverlay'; import {Theme} from '../../ui/styleUtils'; import {VCMetadata} from '../../../shared/VCMetadata'; import {format} from 'date-fns'; -import {EsignetMosipVCItemMachine} from '../../../machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine'; import {VCCardSkeleton} from '../common/VCCardSkeleton'; import {VCCardViewContent} from './VCCardViewContent'; -import {useVcItemController} from '../VcItemController'; +import {useVcItemController} from '../VCItemController'; import {getCredentialIssuersWellKnownConfig} from '../../../shared/openId4VCI/Utils'; import {CARD_VIEW_DEFAULT_FIELDS, isVCLoaded} from '../common/VCUtils'; +import { + VCItemEvents, + VCItemMachine, +} from '../../../machines/VerifiableCredential/VCItemMachine/VCItemMachine'; -export const VCCardView: React.FC< - ExistingVCItemProps | EsignetVCItemProps -> = props => { +export const VCCardView: React.FC = props => { let { service, context, - verifiableCredential, - emptyWalletBindingId, + credential, + verifiableCredentialData, + walletBindingResponse, isKebabPopUp, isSavingFailedInIdle, storeErrorTranslationPath, @@ -37,37 +35,27 @@ export const VCCardView: React.FC< generatedOn && format(new Date(generatedOn), 'MM/dd/yyyy'); useEffect(() => { - service.send( - ExistingMosipVCItemEvents.UPDATE_VC_METADATA(props.vcMetadata), - ); + service.send(VCItemEvents.UPDATE_VC_METADATA(props.vcMetadata)); }, [props.vcMetadata]); - const credential = props.isDownloading - ? null - : new VCMetadata(props.vcMetadata).isFromOpenId4VCI() - ? verifiableCredential?.credential - : verifiableCredential; + const vc = props.isDownloading ? null : credential; const [fields, setFields] = useState([]); const [wellknown, setWellknown] = useState(null); useEffect(() => { getCredentialIssuersWellKnownConfig( - props?.vcMetadata.issuer, - verifiableCredential?.wellKnown, - verifiableCredential?.credentialTypes, + verifiableCredentialData?.issuer, + verifiableCredentialData?.wellKnown, + verifiableCredentialData?.credentialTypes, CARD_VIEW_DEFAULT_FIELDS, ).then(response => { setWellknown(response.wellknown); setFields(response.fields); }); - }, [verifiableCredential?.wellKnown]); + }, [verifiableCredentialData?.wellKnown]); - if (!isVCLoaded(verifiableCredential, fields) || wellknown === null) { - return ( - - - - ); + if (!isVCLoaded(credential, fields)) { + return ; } return ( @@ -83,9 +71,9 @@ export const VCCardView: React.FC< ) => void; - onShow?: (vcRef?: ActorRefFrom) => void; - isDownloading?: boolean; - isPinned?: boolean; - flow?: string; -} - -export interface EsignetVCItemProps { - vcMetadata: VCMetadata; - margin?: string; - selectable?: boolean; - selected?: boolean; - onPress?: (vcRef?: ActorRefFrom) => void; - onShow?: (vcRef?: ActorRefFrom) => void; + onPress?: (vcRef?: ActorRefFrom) => void; + onShow?: (vcRef?: ActorRefFrom) => void; isDownloading?: boolean; isPinned?: boolean; flow?: string; diff --git a/components/VC/Views/VCCardViewContent.tsx b/components/VC/Views/VCCardViewContent.tsx index 36e2cac7..3485ca33 100644 --- a/components/VC/Views/VCCardViewContent.tsx +++ b/components/VC/Views/VCCardViewContent.tsx @@ -1,34 +1,25 @@ import React from 'react'; -import {ImageBackground, Pressable} from 'react-native'; +import {ImageBackground, Pressable, Image} from 'react-native'; import {getLocalizedField} from '../../../i18n'; import {VCMetadata} from '../../../shared/VCMetadata'; - import {KebabPopUp} from '../../KebabPopUp'; -import {VerifiableCredential} from '../../../types/VC/ExistingMosipVC/vc'; +import {VerifiableCredential} from '../../../types/VC/vc'; import {Column, Row} from '../../ui'; import {Theme} from '../../ui/styleUtils'; import {CheckBox, Icon} from 'react-native-elements'; import {SvgImage} from '../../ui/svg'; -import {faceImageSource} from '../../VcItemContainerProfileImage'; -import { - getIssuerLogo, - isVCLoaded, - setBackgroundColour, -} from '../common/VCUtils'; -import {setTextColor} from '../common/VCItemField'; -import {VCItemFieldValue} from '../common/VCItemField'; +import {VcItemContainerProfileImage} from '../../VcItemContainerProfileImage'; +import {isVCLoaded, setBackgroundColour} from '../common/VCUtils'; +import {setTextColor, VCItemFieldValue} from '../common/VCItemField'; import {WalletBinding} from '../../../screens/Home/MyVcs/WalletBinding'; import {VCVerification} from '../../VCVerification'; import {Issuers} from '../../../shared/openId4VCI/Utils'; import {VCItemContainerFlowType} from '../../../shared/Utils'; import {RemoveVcWarningOverlay} from '../../../screens/Home/MyVcs/RemoveVcWarningOverlay'; import {HistoryTab} from '../../../screens/Home/MyVcs/HistoryTab'; -import {VcItemContainerProfileImage} from '../../VcItemContainerProfileImage'; -export const VCCardViewContent: React.FC< - ExistingMosipVCItemContentProps | EsignetMosipVCItemContentProps -> = props => { - const selectableOrCheck = props.selectable && ( +export const VCCardViewContent: React.FC = props => { + const isVCSelectable = props.selectable && ( props.onPress()} /> ); + const issuerLogo = props.verifiableCredentialData.issuerLogo; + const faceImage = props.verifiableCredentialData.face; return ( @@ -74,16 +65,20 @@ export const VCCardViewContent: React.FC< - {!isVCLoaded(props.credential, props.fields) - ? null - : getIssuerLogo( - new VCMetadata(props.vcMetadata).isFromOpenId4VCI(), - props.verifiableCredential?.issuerLogo, - )} + {isVCLoaded(props.credential, props.fields) && ( + {issuerLogo?.alt_text} + )} + {!Object.values(VCItemContainerFlowType).includes(props.flow) && ( <> {props.vcMetadata.issuer === Issuers.Sunbird || - !props.emptyWalletBindingId + props.walletBindingResponse ? SvgImage.walletActivatedIcon() : SvgImage.walletUnActivatedIcon()} )} - {props.credential && selectableOrCheck} + {isVCSelectable} @@ -120,10 +115,10 @@ export const VCCardViewContent: React.FC< ); }; -export interface ExistingMosipVCItemContentProps { +export interface VCItemContentProps { context: any; - verifiableCredential: VerifiableCredential; credential: VerifiableCredential; + verifiableCredentialData: any; fields: []; wellknown: {}; generatedOn: string; @@ -134,29 +129,7 @@ export interface ExistingMosipVCItemContentProps { onPress?: () => void; isDownloading?: boolean; flow?: string; - emptyWalletBindingId: boolean; - KEBAB_POPUP: () => {}; - DISMISS: () => {}; - isKebabPopUp: boolean; - vcMetadata: VCMetadata; - isVerified?: boolean; -} - -export interface EsignetMosipVCItemContentProps { - context: any; - credential: VerifiableCredential; - fields: []; - wellknown: {}; - generatedOn: string; - selectable: boolean; - selected: boolean; - isPinned?: boolean; - service: any; - onPress?: () => void; - isDownloading?: boolean; - flow?: string; - emptyWalletBindingId: boolean; - verifiableCredential: VerifiableCredential; + walletBindingResponse: {}; KEBAB_POPUP: () => {}; DISMISS: () => {}; isKebabPopUp: boolean; diff --git a/components/VC/Views/VCDetailView.tsx b/components/VC/Views/VCDetailView.tsx index ac8857e7..89c7f2a1 100644 --- a/components/VC/Views/VCDetailView.tsx +++ b/components/VC/Views/VCDetailView.tsx @@ -1,19 +1,13 @@ import React, {useEffect, useState} from 'react'; import {useTranslation} from 'react-i18next'; import {Image, ImageBackground, View} from 'react-native'; -import {Icon} from 'react-native-elements'; -import {VC} from '../../../types/VC/ExistingMosipVC/vc'; +import { + VerifiableCredential, + WalletBindingResponse, +} from '../../../types/VC/vc'; import {Button, Column, Row, Text} from '../../ui'; import {Theme} from '../../ui/styleUtils'; import {QrCodeOverlay} from '../../QrCodeOverlay'; -import {VCMetadata} from '../../../shared/VCMetadata'; -import { - VcIdType, - VerifiableCredential, - VerifiablePresentation, -} from '../../../types/VC/EsignetMosipVC/vc'; -import {WalletBindingResponse} from '../../../shared/cryptoutil/cryptoUtil'; -import {logoType} from '../../../machines/issuersMachine'; import {SvgImage} from '../../ui/svg'; import { getDetailedViewFields, @@ -31,40 +25,10 @@ import {setTextColor} from '../common/VCItemField'; import {ActivityIndicator} from '../../ui/ActivityIndicator'; import {ProfileIcon} from '../../ProfileIcon'; -const getIssuerLogo = (isOpenId4VCI: boolean, issuerLogo: logoType) => { - if (isOpenId4VCI) { +const getProfileImage = (face: any) => { + if (face) { return ( - {issuerLogo?.alt_text} - ); - } - return SvgImage.MosipLogo(Theme.Styles.vcDetailsLogo); -}; - -const getProfileImage = ( - props: ExistingVCItemDetailsProps | EsignetVCItemDetailsProps, - verifiableCredential, - isOpenId4VCI, -) => { - if (isOpenId4VCI) { - if (verifiableCredential?.credentialSubject?.face) { - return ( - - ); - } - } else if (props?.vc?.credential?.biometrics?.face) { - return ( - + ); } return ( @@ -75,33 +39,25 @@ const getProfileImage = ( ); }; -export const VCDetailView: React.FC< - ExistingVCItemDetailsProps | EsignetVCItemDetailsProps -> = props => { +export const VCDetailView: React.FC = props => { const {t, i18n} = useTranslation('VcDetails'); - - let isOpenId4VCI = VCMetadata.fromVC(props.vc.vcMetadata).isFromOpenId4VCI(); - const issuerLogo = getIssuerLogo( - isOpenId4VCI, - props.vc?.verifiableCredential?.issuerLogo, - ); - const verifiableCredential = isOpenId4VCI - ? props.vc?.verifiableCredential.credential - : props.vc?.verifiableCredential; - + const logo = props.verifiableCredentialData.issuerLogo; + const face = props.verifiableCredentialData.face; + const verifiableCredential = props.credential; let [fields, setFields] = useState([]); const [wellknown, setWellknown] = useState(null); + useEffect(() => { getDetailedViewFields( - VCMetadata.fromVC(props.vc.vcMetadata).issuer, - props.vc?.verifiableCredential?.wellKnown, - props.vc?.verifiableCredential?.credentialTypes, + props.verifiableCredentialData?.issuer, + props.verifiableCredentialData?.wellKnown, + props.verifiableCredentialData?.credentialTypes, DETAIL_VIEW_DEFAULT_FIELDS, ).then(response => { setWellknown(response.wellknown); setFields(response.fields); }); - }, [props.verifiableCredential?.wellKnown]); + }, [props.verifiableCredentialData?.wellKnown]); const shouldShowHrLine = verifiableCredential => { const availableFieldNames = Object.keys( @@ -141,14 +97,20 @@ export const VCDetailView: React.FC< source={Theme.OpenCard}> - {getProfileImage(props, verifiableCredential, isOpenId4VCI)} + {getProfileImage(face)} - {issuerLogo} + {logo?.alt_text} - {props.vcHasImage ? ( + {props.vcHasImage && ( - {props.activeTab !== 1 ? ( - props.isBindingPending && - isActivationNeeded(props.vc.vcMetadata.issuer) ? ( + {props.activeTab !== 1 && ( + !props.walletBindingResponse && + isActivationNeeded(props.verifiableCredentialData?.issuer)? ( @@ -252,7 +214,9 @@ export const VCDetailView: React.FC< fontSize: 14, }} margin={'0 18 0 0'}> - {isActivationNeeded(props.vc.vcMetadata.issuer) + {isActivationNeeded( + props.verifiableCredentialData?.issuer, + ) ? t('profileAuthenticated') : t('credentialActivated')} @@ -260,45 +224,18 @@ export const VCDetailView: React.FC< ) - ) : ( - <> - )} + ) } - ) : ( - <> - )} + ) } ); }; -export interface ExistingVCItemDetailsProps { - vc: VC; - isBindingPending: boolean; +export interface VCItemDetailsProps { + credential: VerifiableCredential | Credential; + verifiableCredentialData: any; + walletBindingResponse: WalletBindingResponse; onBinding?: () => void; activeTab?: Number; vcHasImage: boolean; } - -export interface EsignetVCItemDetailsProps { - vc: EsignetVC; - isBindingPending: boolean; - onBinding?: () => void; - activeTab?: number; - vcHasImage: boolean; -} - -export interface EsignetVC { - id: string; - idType: VcIdType; - verifiableCredential: VerifiableCredential; - verifiablePresentation?: VerifiablePresentation; - generatedOn: Date; - requestId: string; - isVerified: boolean; - lastVerifiedOn: number; - shouldVerifyPresence?: boolean; - walletBindingResponse?: WalletBindingResponse; - credentialRegistry: string; - isPinned?: boolean; - hashedId: string; -} diff --git a/components/VC/common/VCCardSkeleton.tsx b/components/VC/common/VCCardSkeleton.tsx index 3232dbb4..79981752 100644 --- a/components/VC/common/VCCardSkeleton.tsx +++ b/components/VC/common/VCCardSkeleton.tsx @@ -1,50 +1,52 @@ import {Theme} from '../../ui/styleUtils'; import {Column, Row} from '../../ui'; -import {ImageBackground} from 'react-native'; +import {ImageBackground, View} from 'react-native'; import React from 'react'; import LinearGradient from 'react-native-linear-gradient'; import ShimmerPlaceholder from 'react-native-shimmer-placeholder'; export const VCCardSkeleton = () => { return ( - - - - - + + + + + + + + + - - - - - - + + + + ); }; diff --git a/components/VC/common/VCUtils.tsx b/components/VC/common/VCUtils.tsx index 75449bf6..3cd17d7a 100644 --- a/components/VC/common/VCUtils.tsx +++ b/components/VC/common/VCUtils.tsx @@ -1,18 +1,13 @@ -import { - CredentialSubject, - VerifiableCredential, -} from '../../../types/VC/ExistingMosipVC/vc'; +import {CredentialSubject, VerifiableCredential} from '../../../types/VC/vc'; import i18n, {getLocalizedField} from '../../../i18n'; import {Row} from '../../ui'; import {VCItemField} from './VCItemField'; import React from 'react'; -import {logoType} from '../../../machines/issuersMachine'; -import {Image} from 'react-native'; import {Theme} from '../../ui/styleUtils'; -import {SvgImage} from '../../ui/svg'; import {CREDENTIAL_REGISTRY_EDIT} from 'react-native-dotenv'; import {getIDType} from '../../../shared/openId4VCI/Utils'; import {VCVerification} from '../../VCVerification'; +import {MIMOTO_BASE_URL} from '../../../shared/constants'; export const CARD_VIEW_DEFAULT_FIELDS = ['fullName']; export const DETAIL_VIEW_DEFAULT_FIELDS = [ @@ -179,17 +174,9 @@ export const isVCLoaded = (verifiableCredential: any, fields: string[]) => { return verifiableCredential != null && fields.length > 0; }; -export const getIssuerLogo = (isOpenId4VCI: boolean, issuerLogo: logoType) => { - if (isOpenId4VCI) { - return ( - {issuerLogo?.alt_text} - ); - } - return SvgImage.MosipLogo(Theme.Styles.logo); +export const getMosipLogo = () => { + return { + url: `${MIMOTO_BASE_URL}/inji/mosip-logo.png`, + alt_text: 'a square logo of mosip', + }; }; diff --git a/components/VcItemActivationStatus.tsx b/components/VcItemActivationStatus.tsx deleted file mode 100644 index e0b71de5..00000000 --- a/components/VcItemActivationStatus.tsx +++ /dev/null @@ -1,135 +0,0 @@ -import React from 'react'; -import {useTranslation} from 'react-i18next'; -import {Dimensions} from 'react-native'; -import {Icon} from 'react-native-elements'; -import {ActorRefFrom} from 'xstate'; -import {vcItemMachine} from '../machines/vcItem'; -import {VerifiableCredential} from '../types/vc'; -import {Row, Text} from './ui'; -import {Theme} from './ui/styleUtils'; - -const WalletUnverifiedIcon: React.FC = () => { - return ( - - ); -}; - -const WalletVerifiedIcon: React.FC = () => { - return ( - - ); -}; - -const WalletUnverifiedActivationDetails: React.FC< - WalletUnVerifiedDetailsProps -> = props => { - const {t} = useTranslation('VcDetails'); - return ( - - - {props.verifiableCredential && } - - - - ); -}; - - -const WalletVerifiedActivationDetails: React.FC = ( - props -) => { - const { t } = useTranslation('VcDetails'); - return ( - - - - - - - ); -}; - -export const VcItemActivationStatus: React.FC = ( - props -) => { - - return ( - - {props.emptyWalletBindingId ? ( - - ) : ( - - )} - - ); -}; - -interface VcItemActivationStatusProps { - showOnlyBindedVc: boolean; - onPress: (vcRef?: ActorRefFrom) => void; - verifiableCredential: VerifiableCredential; - emptyWalletBindingId: boolean; -} - -interface WalletVerifiedDetailsProps { - showOnlyBindedVc: boolean; - onPress: (vcRef?: ActorRefFrom) => void; - verifiableCredential: VerifiableCredential; -} - -interface WalletUnVerifiedDetailsProps { - onPress: (vcRef?: ActorRefFrom) => void; - verifiableCredential: VerifiableCredential; -} diff --git a/components/VcItemContainerProfileImage.tsx b/components/VcItemContainerProfileImage.tsx index da24e1e3..fc65104d 100644 --- a/components/VcItemContainerProfileImage.tsx +++ b/components/VcItemContainerProfileImage.tsx @@ -1,19 +1,12 @@ -import { - EsignetMosipVCItemContentProps, - ExistingMosipVCItemContentProps, -} from './VC/Views/VCCardViewContent'; -import {VerifiableCredential} from '../types/VC/ExistingMosipVC/vc'; +import {VCItemContentProps} from './VC/Views/VCCardViewContent'; import {ImageBackground} from 'react-native'; import {Theme} from './ui/styleUtils'; import React from 'react'; import {ProfileIcon} from './ProfileIcon'; -import {VCMetadata} from '../shared/VCMetadata'; import {SvgImage} from './ui/svg'; -export const VcItemContainerProfileImage = ( - props: ExistingMosipVCItemContentProps | EsignetMosipVCItemContentProps, -) => { - const imageUri = faceImageSource(props); +export const VcItemContainerProfileImage = (props: VCItemContentProps) => { + const imageUri = props.verifiableCredentialData.face; return imageUri ? ( ); }; - -export function faceImageSource(props: faceImageSourceProps) { - return new VCMetadata(props?.vcMetadata)?.isFromOpenId4VCI() - ? props?.credential?.credentialSubject?.face - : props?.context?.credential?.biometrics?.face; -} - -interface faceImageSourceProps { - vcMetadata: VCMetadata; - credential: VerifiableCredential; - context: any; -} diff --git a/components/VcItemContent.tsx b/components/VcItemContent.tsx deleted file mode 100644 index 7c924665..00000000 --- a/components/VcItemContent.tsx +++ /dev/null @@ -1,205 +0,0 @@ -import React from 'react'; -import {useTranslation} from 'react-i18next'; -import {Image, ImageBackground, View} from 'react-native'; -import {getLocalizedField} from '../i18n'; -import {VerifiableCredential} from '../types/vc'; -import {VcItemTags} from './VcItemTags'; -import VerifiedIcon from './VerifiedIcon'; -import {Column, Row, Text} from './ui'; -import {Theme} from './ui/styleUtils'; -import {CheckBox, Icon} from 'react-native-elements'; - -const getDetails = (arg1, arg2, verifiableCredential) => { - if (arg1 === 'Status') { - return ( - - - {arg1} - - - - {!verifiableCredential ? '' : arg2} - - {!verifiableCredential ? null : } - - - ); - } else { - return ( - - - {arg1} - - - {!verifiableCredential ? '' : arg2} - - - ); - } -}; - -export const VcItemContent: React.FC = (props) => { - //Assigning the UIN and VID from the VC details to display the idtype label - const uin = props.verifiableCredential?.credentialSubject.UIN; - const vid = props.verifiableCredential?.credentialSubject.VID; - const fullName = !props.verifiableCredential - ? '' - : getLocalizedField(props.verifiableCredential.credentialSubject.fullName); - const { t } = useTranslation('VcDetails'); - const isvalid = !props.verifiableCredential ? '' : t('valid'); - const selectableOrCheck = props.selectable ? ( - } - uncheckedIcon={} - onPress={() => props.onPress()} - /> - ) : null; - - return ( - - - - - - {props.iconName && ( - - )} - - - {getDetails(t('fullName'), fullName, props.verifiableCredential)} - - - - {t('idType')} - - - {t('nationalCard')} - - - - - - - {props.verifiableCredential ? selectableOrCheck : null} - - - - - - {uin ? getDetails(t('uin'), uin, props.verifiableCredential) : null} - {vid ? getDetails(t('vid'), vid, props.verifiableCredential) : null} - {!props.verifiableCredential - ? getDetails(t('id'), uin || vid, props.verifiableCredential) - : null} - {getDetails( - t('generatedOn'), - props.generatedOn, - props.verifiableCredential - )} - - - {props.verifiableCredential - ? getDetails(t('status'), isvalid, props.verifiableCredential) - : null} - - - - - - - - - ); -}; - -interface VcItemContentProps { - context: any; - verifiableCredential: VerifiableCredential; - generatedOn: string; - tag: string; - selectable: boolean; - selected: boolean; - iconName?: string; - iconType?: string; - service: any; - onPress?: () => void; -} diff --git a/components/VidItem.tsx b/components/VidItem.tsx deleted file mode 100644 index a0f98bad..00000000 --- a/components/VidItem.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import React, {useContext, useRef} from 'react'; -import {useInterpret, useSelector} from '@xstate/react'; -import {Pressable} from 'react-native'; -import {CheckBox} from 'react-native-elements'; -import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; -import {ActorRefFrom} from 'xstate'; -import { - selectVerifiableCredential, - selectGeneratedOn, -} from '../machines/VCItemMachine/commonSelectors'; -import { - createExistingMosipVCItemMachine, - selectId, - ExistingMosipVCItemMachine, -} from '../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine'; -import {Column, Row, Text} from './ui'; -import {Theme} from './ui/styleUtils'; -import {RotatingIcon} from './RotatingIcon'; -import {GlobalContext} from '../shared/GlobalContext'; -import {getLocalizedField} from '../i18n'; -import {VCMetadata} from '../shared/VCMetadata'; - -export const VidItem: React.FC = props => { - const {appService} = useContext(GlobalContext); - const machine = useRef( - createExistingMosipVCItemMachine( - appService.getSnapshot().context.serviceRefs, - props.vcMetadata, - ), - ); - const service = useInterpret(machine.current); - const uin = useSelector(service, selectId); - const verifiableCredential = useSelector(service, selectVerifiableCredential); - const generatedOn = useSelector(service, selectGeneratedOn); - - const selectableOrCheck = props.selectable ? ( - } - uncheckedIcon={} - onPress={() => props.onPress(service)} - /> - ) : ( - - ); - - return ( - props.onPress(service)} - disabled={!verifiableCredential}> - - - - {!verifiableCredential ? '' : uin} - - - {!verifiableCredential - ? '' - : getLocalizedField( - verifiableCredential.credentialSubject.fullName, - ) + - ' · ' + - generatedOn} - - - {verifiableCredential ? ( - selectableOrCheck - ) : ( - - )} - - - ); -}; - -interface VcItemProps { - vcMetadata: VCMetadata; - margin?: string; - selectable?: boolean; - selected?: boolean; - onPress?: (vcRef?: ActorRefFrom) => void; -} diff --git a/components/WalletBindingSuccessController.tsx b/components/WalletBindingSuccessController.tsx index 52ec79c6..688ae140 100644 --- a/components/WalletBindingSuccessController.tsx +++ b/components/WalletBindingSuccessController.tsx @@ -4,7 +4,7 @@ import {useSelector} from '@xstate/react'; import { VcEvents, selectWalletBindingSuccess, -} from '../machines/VCItemMachine/vc'; +} from '../machines/VerifiableCredential/VCMetaMachine/vc'; export const UseWalletBindingSuccess = () => { const {appService} = useContext(GlobalContext); diff --git a/components/kebabMenuUtils.ts b/components/kebabMenuUtils.ts index 58feec3b..a430376b 100644 --- a/components/kebabMenuUtils.ts +++ b/components/kebabMenuUtils.ts @@ -3,13 +3,10 @@ import {SvgImage} from './ui/svg'; import {useKebabPopUp} from './KebabPopUpController'; import {isActivationNeeded} from '../shared/openId4VCI/Utils'; import {VCShareFlowType} from '../shared/Utils'; -import {useNavigation} from '@react-navigation/native'; -import {BOTTOM_TAB_ROUTES} from '../routes/routesConstants'; export const getKebabMenuOptions = props => { const controller = useKebabPopUp(props); const {t} = useTranslation('HomeScreenKebabPopUp'); - const navigation = useNavigation(); const loadScanScreen = flowType => () => { controller.SELECT_VC_ITEM(props.service, flowType), @@ -18,7 +15,7 @@ export const getKebabMenuOptions = props => { }; const activationNotCompleted = - controller.emptyWalletBindingId && + !controller.walletBindingResponse && isActivationNeeded(props?.vcMetadata.issuer); const vcActionsList = [ @@ -38,7 +35,6 @@ export const getKebabMenuOptions = props => { label: t('viewActivityLog'), icon: SvgImage.OutlinedScheduleIcon(), onPress: controller.SHOW_ACTIVITY, - testID: 'viewActivityLog', }, { @@ -66,7 +62,7 @@ export const getKebabMenuOptions = props => { onPress: activationNotCompleted ? controller.ADD_WALLET_BINDING_ID : loadScanScreen(VCShareFlowType.MINI_VIEW_QR_LOGIN), - testID: 'pinOrUnPinCard', + testID: 'pendingActivationOrActivated', }; if (props.vcHasImage) { diff --git a/components/ui/Modal.tsx b/components/ui/Modal.tsx index 8de8b632..6a346b11 100644 --- a/components/ui/Modal.tsx +++ b/components/ui/Modal.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import {I18nManager, Pressable, Modal as RNModal, View} from 'react-native'; +import {I18nManager, Modal as RNModal, Pressable, View} from 'react-native'; import {Icon} from 'react-native-elements'; import {Column, Row, Text} from '.'; import {useSendVcScreen} from '../../screens/Scan/SendVcScreenController'; @@ -109,5 +109,5 @@ export interface ModalProps { headerLeft?: React.ReactElement; arrowLeft?: boolean; onShow?: () => void; - children: React.ReactNode; + children?: React.ReactNode; } diff --git a/components/ui/svg.tsx b/components/ui/svg.tsx index a8b8f845..b09ba5f3 100644 --- a/components/ui/svg.tsx +++ b/components/ui/svg.tsx @@ -200,6 +200,7 @@ export class SvgImage { /> ); } + static settings(focused: boolean) { //NOTE: Here tab icons names should be same with key "name" in main.ts return ( @@ -463,6 +464,7 @@ export class SvgImage { /> ); } + static ICloudIcon(width, height) { return ( ); } + static SearchIcon() { return ; } diff --git a/components/ui/themes/DefaultTheme.ts b/components/ui/themes/DefaultTheme.ts index 19035052..e06305ce 100644 --- a/components/ui/themes/DefaultTheme.ts +++ b/components/ui/themes/DefaultTheme.ts @@ -1337,31 +1337,6 @@ export const DefaultTheme = { borderRadius: 16, }, }), - VidItemStyles: StyleSheet.create({ - title: { - color: Colors.Black, - backgroundColor: 'transparent', - }, - loadingTitle: { - color: 'transparent', - backgroundColor: Colors.Grey5, - borderRadius: 4, - }, - subtitle: { - backgroundColor: 'transparent', - }, - loadingSubtitle: { - backgroundColor: Colors.Grey, - borderRadius: 4, - }, - container: { - backgroundColor: Colors.White, - }, - loadingContainer: { - backgroundColor: Colors.Grey6, - borderRadius: 4, - }, - }), OnboardingOverlayStyles: StyleSheet.create({ overlay: { padding: 24, diff --git a/components/ui/themes/PurpleTheme.ts b/components/ui/themes/PurpleTheme.ts index f0af2f8d..44bd87d2 100644 --- a/components/ui/themes/PurpleTheme.ts +++ b/components/ui/themes/PurpleTheme.ts @@ -1339,31 +1339,6 @@ export const PurpleTheme = { borderRadius: 16, }, }), - VidItemStyles: StyleSheet.create({ - title: { - color: Colors.Black, - backgroundColor: 'transparent', - }, - loadingTitle: { - color: 'transparent', - backgroundColor: Colors.Grey5, - borderRadius: 4, - }, - subtitle: { - backgroundColor: 'transparent', - }, - loadingSubtitle: { - backgroundColor: Colors.Grey, - borderRadius: 4, - }, - container: { - backgroundColor: Colors.White, - }, - loadingContainer: { - backgroundColor: Colors.Grey6, - borderRadius: 4, - }, - }), OnboardingOverlayStyles: StyleSheet.create({ overlay: { padding: 24, diff --git a/i18n.ts b/i18n.ts index 824de820..5df49d2d 100644 --- a/i18n.ts +++ b/i18n.ts @@ -10,9 +10,10 @@ import kn from './locales/kan.json'; import ta from './locales/tam.json'; import {iso6393To1} from 'iso-639-3'; -import {LocalizedField} from './types/VC/ExistingMosipVC/vc'; + import Keychain from 'react-native-keychain'; import {getItem} from './machines/store'; +import {LocalizedField} from './types/VC/vc'; const resources = {en, fil, ar, hi, kn, ta}; const locale = Localization.locale; diff --git a/machines/QrLoginMachine.ts b/machines/QrLoginMachine.ts index 049414cd..0a34abcf 100644 --- a/machines/QrLoginMachine.ts +++ b/machines/QrLoginMachine.ts @@ -14,7 +14,7 @@ import { MY_VCS_STORE_KEY, } from '../shared/constants'; import {StoreEvents} from './store'; -import {linkTransactionResponse, VC} from '../types/VC/ExistingMosipVC/vc'; +import {linkTransactionResponse, VC} from '../types/VC/vc'; import {request} from '../shared/request'; import { getJWT, @@ -31,9 +31,9 @@ import { sendEndEvent, } from '../shared/telemetry/TelemetryUtils'; import {TelemetryConstants} from '../shared/telemetry/TelemetryConstants'; -import {API_URLS} from '../shared/api'; -import getAllConfigurations from '../shared/api'; +import getAllConfigurations, {API_URLS} from '../shared/api'; import {VCShareFlowType} from '../shared/Utils'; +import {getMosipLogo} from '../components/VC/common/VCUtils'; const model = createModel( { @@ -598,6 +598,7 @@ type State = StateFrom; export function selectMyVcs(state: State) { return state.context.myVcs; } + export function selectIsWaitingForData(state: State) { return state.matches('waitingForData'); } @@ -633,6 +634,7 @@ export function selectIsShowError(state: State) { export function selectIsRequestConsent(state: State) { return state.matches('requestConsent'); } + export function selectIsSendingAuthenticate(state: State) { return state.matches('sendingAuthenticate'); } @@ -645,13 +647,37 @@ export function selectIsVerifyingSuccesful(state: State) { return state.matches('success'); } -export function selectSelectedVc(state: State) { - return state.context.selectedVc; +export function selectCredential(state: State) { + return new VCMetadata(state.context.selectedVc?.vcMetadata).isFromOpenId4VCI() + ? state.context.selectedVc?.verifiableCredential?.credential + : state.context.selectedVc?.credential; +} + +export function selectVerifiableCredentialData(state: State) { + const vcMetadata = new VCMetadata(state.context.selectedVc?.vcMetadata); + return vcMetadata.isFromOpenId4VCI() + ? { + vcMetadata: vcMetadata, + face: state.context.selectedVc?.verifiableCredential?.credential + ?.credentialSubject?.face, + issuerLogo: state.context.selectedVc?.verifiableCredential?.issuerLogo, + wellKnown: state.context.selectedVc?.verifiableCredential?.wellKnown, + credentialTypes: + state.context.selectedVc?.verifiableCredential?.credentialTypes, + issuer: vcMetadata.issuer, + } + : { + vcMetadata: vcMetadata, + issuer: vcMetadata.issuer, + face: state.context.selectedVc?.credential?.biometrics?.face, + issuerLogo: getMosipLogo(), + }; } export function selectLinkTransactionResponse(state: State) { return state.context.linkTransactionResponse; } + export function selectEssentialClaims(state: State) { return state.context.essentialClaims; } @@ -671,6 +697,7 @@ export function selectClientName(state: State) { export function selectErrorMessage(state: State) { return state.context.errorMessage; } + export function selectIsSharing(state: State) { return state.context.isSharing; } diff --git a/machines/QrLoginMachine.typegen.ts b/machines/QrLoginMachine.typegen.ts index 682a283c..9dfefd74 100644 --- a/machines/QrLoginMachine.typegen.ts +++ b/machines/QrLoginMachine.typegen.ts @@ -48,20 +48,20 @@ export interface Typegen0 { loadMyVcs: 'done.invoke.QrLogin.linkTransaction:invocation[0]'; loadThumbprint: 'FACE_VALID'; resetFlowType: 'xstate.init'; - logValue: 'VERIFY'; resetLinkTransactionId: 'GET'; resetSelectedVc: 'xstate.init'; resetSelectedVoluntaryClaims: 'GET'; setClaims: 'done.invoke.QrLogin.linkTransaction:invocation[0]'; setConsentClaims: 'TOGGLE_CONSENT_CLAIM'; + setFaceAuthConsent: 'GET'; setLinkedTransactionId: 'done.invoke.QrLogin.sendingAuthenticate:invocation[0]'; setMyVcs: 'STORE_RESPONSE'; setScanData: 'GET'; setSelectedVc: 'SELECT_VC'; - setShowAgainConsent: 'FACE_VERIFICATION_CONSENT'; + setShowFaceAuthConsent: 'FACE_VERIFICATION_CONSENT'; setThumbprint: 'STORE_RESPONSE'; setlinkTransactionResponse: 'done.invoke.QrLogin.linkTransaction:invocation[0]'; - storeContext: 'FACE_VERIFICATION_CONSENT'; + storeShowFaceAuthConsent: 'FACE_VERIFICATION_CONSENT'; }; eventsCausingDelays: {}; eventsCausingGuards: { @@ -69,7 +69,7 @@ export interface Typegen0 { isSimpleShareFlow: | 'CANCEL' | 'done.invoke.QrLogin.linkTransaction:invocation[0]'; - isConsentGiven: 'VERIFY'; + showFaceAuthConsentScreen: 'VERIFY'; }; eventsCausingServices: { linkTransaction: 'GET'; diff --git a/machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine.ts b/machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine.ts deleted file mode 100644 index 1fc5a724..00000000 --- a/machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine.ts +++ /dev/null @@ -1,870 +0,0 @@ -import {assign, ErrorPlatformEvent, EventFrom, send} from 'xstate'; -import {createModel} from 'xstate/lib/model'; -import {AppServices} from '../../../shared/GlobalContext'; -import {VCMetadata} from '../../../shared/VCMetadata'; -import {VC} from '../../../types/VC/ExistingMosipVC/vc'; -import { - generateKeys, - isHardwareKeystoreExists, - WalletBindingResponse, -} from '../../../shared/cryptoutil/cryptoUtil'; -import {getIdType, Protocols} from '../../../shared/openId4VCI/Utils'; -import {StoreEvents} from '../../../machines/store'; -import {MIMOTO_BASE_URL, MY_VCS_STORE_KEY} from '../../../shared/constants'; -import {VcEvents} from '../vc'; -import i18n from '../../../i18n'; -import {KeyPair} from 'react-native-rsa-native'; -import { - getBindingCertificateConstant, - savePrivateKey, -} from '../../../shared/keystore/SecureKeystore'; -import {ActivityLogEvents} from '../../../machines/activityLog'; -import {request} from '../../../shared/request'; -import SecureKeystore from '@mosip/secure-keystore'; -import {VerifiableCredential} from './vc'; -import { - getEndEventData, - getInteractEventData, - getStartEventData, - sendEndEvent, - sendInteractEvent, - sendStartEvent, -} from '../../../shared/telemetry/TelemetryUtils'; -import {TelemetryConstants} from '../../../shared/telemetry/TelemetryConstants'; - -import {API_URLS} from '../../../shared/api'; -import {getHomeMachineService} from '../../../screens/Home/HomeScreenController'; -import {BackupEvents} from '../../backupAndRestore/backup'; -import Cloud, { - isSignedInResult, -} from '../../../shared/CloudBackupAndRestoreUtils'; -import {getMosipIdentifier} from '../../../shared/commonUtil'; - -const model = createModel( - { - serviceRefs: {} as AppServices, - vcMetadata: {} as VCMetadata, - generatedOn: new Date() as Date, - verifiableCredential: null as VerifiableCredential, - hashedId: '', - publicKey: '', - privateKey: '', - otp: '', - otpError: '', - idError: '', - transactionId: '', - bindingTransactionId: '', - walletBindingResponse: null as WalletBindingResponse, - tempWalletBindingIdResponse: null as WalletBindingResponse, - walletBindingError: '', - isMachineInKebabPopupState: false, - bindingAuthFailedMessage: '' as string, - phoneNumber: '' as string, - email: '' as string, - }, - { - events: { - KEY_RECEIVED: (key: string) => ({key}), - KEY_ERROR: (error: Error) => ({error}), - STORE_READY: () => ({}), - DISMISS: () => ({}), - CREDENTIAL_DOWNLOADED: (vc: VC) => ({vc}), - STORE_RESPONSE: (response: VC) => ({response}), - POLL: () => ({}), - DOWNLOAD_READY: () => ({}), - GET_VC_RESPONSE: (vc: VC) => ({vc}), - VERIFY: () => ({}), - RESEND_OTP: () => ({}), - INPUT_OTP: (otp: string) => ({otp}), - REFRESH: () => ({}), - ADD_WALLET_BINDING_ID: () => ({}), - CANCEL: () => ({}), - CONFIRM: () => ({}), - STORE_ERROR: (error: Error) => ({error}), - PIN_CARD: () => ({}), - KEBAB_POPUP: () => ({}), - SHOW_ACTIVITY: () => ({}), - CLOSE_VC_MODAL: () => ({}), - REMOVE: (vcMetadata: VCMetadata) => ({vcMetadata}), - UPDATE_VC_METADATA: (vcMetadata: VCMetadata) => ({vcMetadata}), - SHOW_BINDING_STATUS: () => ({}), - }, - }, -); - -export const EsignetMosipVCItemEvents = model.events; - -export const EsignetMosipVCItemMachine = model.createMachine( - { - predictableActionArguments: true, - preserveActionOrder: true, - tsTypes: {} as import('./EsignetMosipVCItemMachine.typegen').Typegen0, - schema: { - context: model.initialContext, - events: {} as EventFrom, - }, - on: { - REFRESH: { - target: '.checkingStore', - }, - UPDATE_VC_METADATA: { - actions: 'setVcMetadata', - }, - }, - description: 'VC', - id: 'vc-item-openid4vci', - initial: 'checkingVc', - states: { - checkingVc: { - entry: ['requestVcContext'], - description: 'Fetch the VC data from the Memory.', - on: { - GET_VC_RESPONSE: [ - { - actions: [ - 'setVerifiableCredential', - 'setContext', - 'setGeneratedOn', - ], - cond: 'hasCredential', - target: 'idle', - }, - { - target: 'checkingStore', - }, - ], - }, - }, - checkingStore: { - entry: 'requestStoredContext', - description: 'Check if VC data is in secured local storage.', - on: { - STORE_RESPONSE: { - actions: ['setVerifiableCredential', 'setContext', 'updateVc'], - target: 'idle', - }, - }, - }, - showBindingWarning: { - on: { - CONFIRM: { - actions: 'sendActivationStartEvent', - target: 'requestingBindingOtp', - }, - CANCEL: [ - { - cond: context => context.isMachineInKebabPopupState, - target: '#vc-item-openid4vci.kebabPopUp', - }, - { - target: 'idle', - }, - ], - }, - }, - requestingBindingOtp: { - invoke: { - src: 'requestBindingOtp', - onDone: [ - { - target: 'acceptingBindingOtp', - actions: ['setPhoneNumber', 'setEmail'], - }, - ], - onError: [ - { - actions: ['setWalletBindingError', 'logWalletBindingFailure'], - target: 'showingWalletBindingError', - }, - ], - }, - }, - showingWalletBindingError: { - on: { - CANCEL: [ - { - cond: context => context.isMachineInKebabPopupState, - actions: ['setWalletBindingErrorEmpty'], - target: '#vc-item-openid4vci.kebabPopUp', - }, - { - actions: ['setWalletBindingErrorEmpty'], - target: 'idle', - }, - ], - }, - }, - acceptingBindingOtp: { - entry: ['clearOtp'], - on: { - INPUT_OTP: { - target: 'addKeyPair', - actions: ['setOtp'], - }, - DISMISS: [ - { - cond: context => context.isMachineInKebabPopupState, - target: '#vc-item-openid4vci.kebabPopUp', - actions: [ - 'sendActivationFailedEndEvent', - 'clearOtp', - 'clearTransactionId', - ], - }, - { - target: 'idle', - actions: [ - 'sendActivationFailedEndEvent', - 'clearOtp', - 'clearTransactionId', - ], - }, - ], - RESEND_OTP: { - target: '.resendOTP', - }, - }, - initial: 'idle', - states: { - idle: {}, - resendOTP: { - invoke: { - src: 'requestBindingOtp', - onDone: { - target: 'idle', - actions: ['setPhoneNumber', 'setEmail'], - }, - onError: { - actions: 'setWalletBindingError', - target: '#vc-item-openid4vci.showingWalletBindingError', - }, - }, - }, - }, - }, - addKeyPair: { - invoke: { - src: 'generateKeyPair', - onDone: [ - { - cond: 'isCustomSecureKeystore', - target: 'addingWalletBindingId', - actions: ['setPublicKey'], - }, - { - target: 'addingWalletBindingId', - actions: ['setPublicKey', 'setPrivateKey'], - }, - ], - onError: [ - { - actions: ['setWalletBindingError', 'logWalletBindingFailure'], - target: 'showingWalletBindingError', - }, - ], - }, - }, - addingWalletBindingId: { - invoke: { - src: 'addWalletBindnigId', - onDone: [ - { - cond: 'isCustomSecureKeystore', - target: 'updatingContextVariables', - }, - { - target: 'updatingPrivateKey', - /*The walletBindingResponse is used for conditional rendering in wallet binding. - However, it wrongly considers activation as successful even when there's an error - in updatingPrivateKey state. So created a temporary context variable to store the binding - response and use it in updatingPrivateKey state*/ - actions: 'setTempWalletBindingResponse', - }, - ], - onError: [ - { - actions: ['setWalletBindingError', 'logWalletBindingFailure'], - target: 'showingWalletBindingError', - }, - ], - }, - }, - - updatingPrivateKey: { - invoke: { - src: 'updatePrivateKey', - onDone: { - target: 'updatingContextVariables', - }, - onError: { - actions: [ - 'setWalletBindingError', - 'logWalletBindingFailure', - 'sendActivationFailedEndEvent', - ], - target: 'showingWalletBindingError', - }, - }, - }, - - updatingContextVariables: { - entry: [ - 'setWalletBindingId', - 'setThumbprintForWalletBindingId', - 'storeContext', - 'updatePrivateKey', - 'updateVc', - 'setWalletBindingErrorEmpty', - 'sendActivationSuccessEvent', - 'logWalletBindingSuccess', - send('SHOW_BINDING_STATUS'), - ], - on: { - SHOW_BINDING_STATUS: [ - { - cond: context => context.isMachineInKebabPopupState, - actions: 'sendWalletBindingSuccess', - target: '#vc-item-openid4vci.kebabPopUp', - }, - { - actions: 'sendWalletBindingSuccess', - target: 'idle', - }, - ], - }, - }, - idle: { - on: { - DISMISS: { - target: 'checkingVc', - }, - KEBAB_POPUP: { - target: 'kebabPopUp', - }, - ADD_WALLET_BINDING_ID: { - target: 'showBindingWarning', - }, - PIN_CARD: { - target: 'pinCard', - actions: 'setPinCard', - }, - }, - }, - pinCard: { - entry: 'sendVcUpdated', - always: { - target: 'idle', - }, - }, - kebabPopUp: { - entry: assign({isMachineInKebabPopupState: () => true}), - exit: assign({isMachineInKebabPopupState: () => false}), - on: { - DISMISS: { - actions: assign({ - isMachineInKebabPopupState: () => false, - }), - target: 'idle', - }, - ADD_WALLET_BINDING_ID: { - target: '#vc-item-openid4vci.showBindingWarning', - }, - PIN_CARD: { - target: '#vc-item-openid4vci.pinCard', - actions: 'setPinCard', - }, - SHOW_ACTIVITY: { - target: '#vc-item-openid4vci.kebabPopUp.showActivities', - }, - REMOVE: { - actions: 'setVcKey', - target: '#vc-item-openid4vci.kebabPopUp.removeWallet', - }, - CLOSE_VC_MODAL: { - actions: ['closeViewVcModal'], - target: '#vc-item-openid4vci', - }, - }, - initial: 'idle', - states: { - idle: {}, - showActivities: { - on: { - DISMISS: '#vc-item-openid4vci', - }, - }, - removeWallet: { - on: { - CONFIRM: { - target: 'removingVc', - }, - CANCEL: { - target: '#vc-item-openid4vci', - }, - }, - }, - removingVc: { - entry: 'removeVcItem', - on: { - STORE_RESPONSE: { - actions: ['closeViewVcModal', 'removedVc', 'logVCremoved'], - target: 'triggerAutoBackup', - }, - }, - }, - triggerAutoBackup: { - invoke: { - src: 'isUserSignedAlready', - onDone: [ - { - cond: 'isSignedIn', - actions: ['sendBackupEvent', 'removedVc', 'logVCremoved'], - target: '#vc-item-openid4vci', - }, - { - actions: ['removedVc', 'logVCremoved'], - target: '#vc-item-openid4vci', - }, - ], - }, - }, - }, - }, - }, - }, - { - actions: { - setVcMetadata: assign({ - vcMetadata: (_, event) => event.vcMetadata, - }), - - requestVcContext: send( - context => ({ - type: 'GET_VC_ITEM', - vcMetadata: context.vcMetadata, - protocol: Protocols.OpenId4VCI, - }), - { - to: context => context.serviceRefs.vc, - }, - ), - requestStoredContext: send( - context => { - return StoreEvents.GET( - VCMetadata.fromVC(context.vcMetadata).getVcKey(), - ); - }, - { - to: context => context.serviceRefs.store, - }, - ), - updateVc: send( - context => { - const {serviceRefs, ...verifiableCredential} = context; - return { - type: 'VC_DOWNLOADED_FROM_OPENID4VCI', - vc: verifiableCredential, - vcMetadata: context.vcMetadata, - }; - }, - { - to: context => context.serviceRefs.vc, - }, - ), - - setVerifiableCredential: model.assign({ - verifiableCredential: (_, event) => { - if (event.type === 'GET_VC_RESPONSE') { - return event.vc.verifiableCredential - ? event.vc.verifiableCredential - : event.vc; - } - return event.response.verifiableCredential; - }, - }), - - setContext: model.assign((context, event) => { - if (event.type === 'STORE_RESPONSE') { - const {verifiableCredential, ...data} = event.response; - return {...context, ...data}; - } - if (event.type === 'GET_VC_RESPONSE') { - const {verifiableCredential, ...data} = event.vc; - return {...context, ...data}; - } - return context; - }), - - setGeneratedOn: model.assign({ - generatedOn: (_context, event) => { - if (event.type === 'GET_VC_RESPONSE') { - return event.vc.generatedOn; - } - return event.response.generatedOn; - }, - }), - - storeContext: send( - context => { - const { - serviceRefs, - isMachineInKebabPopupState, - tempWalletBindingIdResponse, - ...data - } = context; - data.credentialRegistry = MIMOTO_BASE_URL; - return StoreEvents.SET( - VCMetadata.fromVC(context.vcMetadata).getVcKey(), - data, - ); - }, - { - to: context => context.serviceRefs.store, - }, - ), - - setPinCard: assign({ - vcMetadata: context => - new VCMetadata({ - ...context.vcMetadata, - isPinned: !context.vcMetadata.isPinned, - }), - }), - - sendBackupEvent: send(BackupEvents.DATA_BACKUP(true), { - to: context => context.serviceRefs.backup, - }), - - sendVcUpdated: send( - context => VcEvents.VC_METADATA_UPDATED(context.vcMetadata), - { - to: context => context.serviceRefs.vc, - }, - ), - - setWalletBindingError: assign({ - walletBindingError: () => - i18n.t(`errors.genericError`, { - ns: 'common', - }), - bindingAuthFailedMessage: (_context, event) => { - const error = JSON.parse(JSON.stringify(event.data)).name; - if (error) { - return error; - } - return ''; - }, - }), - - setWalletBindingErrorEmpty: assign({ - walletBindingError: () => '', - bindingAuthFailedMessage: () => '', - }), - - sendWalletBindingSuccess: send( - context => { - return { - type: 'WALLET_BINDING_SUCCESS', - }; - }, - { - to: context => context.serviceRefs.vc, - }, - ), - - sendActivationStartEvent: context => { - sendStartEvent( - getStartEventData( - context.isMachineInKebabPopupState - ? TelemetryConstants.FlowType.vcActivationFromKebab - : TelemetryConstants.FlowType.vcActivation, - ), - ); - sendInteractEvent( - getInteractEventData( - context.isMachineInKebabPopupState - ? TelemetryConstants.FlowType.vcActivationFromKebab - : TelemetryConstants.FlowType.vcActivation, - TelemetryConstants.InteractEventSubtype.click, - 'Activate Button', - ), - ); - }, - - sendActivationFailedEndEvent: (context, event, meta) => { - const [errorId, errorMessage] = - event.data?.message === 'Could not store private key in keystore' - ? [ - TelemetryConstants.ErrorId.updatePrivateKey, - TelemetryConstants.ErrorMessage.privateKeyUpdationFailed, - ] - : [ - TelemetryConstants.ErrorId.userCancel, - TelemetryConstants.ErrorMessage.activationCancelled, - ]; - sendEndEvent( - getEndEventData( - context.isMachineInKebabPopupState - ? TelemetryConstants.FlowType.vcActivationFromKebab - : TelemetryConstants.FlowType.vcActivation, - TelemetryConstants.EndEventStatus.failure, - { - errorId: errorId, - errorMessage: errorMessage, - }, - ), - ); - }, - - sendActivationSuccessEvent: context => - sendEndEvent( - getEndEventData( - context.isMachineInKebabPopupState - ? TelemetryConstants.FlowType.vcActivationFromKebab - : TelemetryConstants.FlowType.vcActivation, - TelemetryConstants.EndEventStatus.success, - ), - ), - - setPublicKey: assign({ - publicKey: (context, event) => { - if (!isHardwareKeystoreExists) { - return (event.data as KeyPair).public; - } - return event.data as string; - }, - }), - - setPrivateKey: assign({ - privateKey: (context, event) => (event.data as KeyPair).private, - }), - - updatePrivateKey: assign({ - privateKey: () => '', - }), - setWalletBindingId: assign({ - walletBindingResponse: (context, event) => { - return isHardwareKeystoreExists - ? (event.data as WalletBindingResponse) - : context.tempWalletBindingIdResponse; - }, - }), - setTempWalletBindingResponse: assign({ - tempWalletBindingIdResponse: (context, event) => - event.data as WalletBindingResponse, - }), - setThumbprintForWalletBindingId: send( - context => { - const {walletBindingResponse} = context; - const walletBindingIdKey = getBindingCertificateConstant( - walletBindingResponse.walletBindingId, - ); - return StoreEvents.SET( - walletBindingIdKey, - walletBindingResponse.thumbprint, - ); - }, - { - to: context => context.serviceRefs.store, - }, - ), - - removedVc: send( - () => ({ - type: 'REFRESH_MY_VCS', - }), - { - to: context => context.serviceRefs.vc, - }, - ), - logWalletBindingSuccess: send( - context => - ActivityLogEvents.LOG_ACTIVITY({ - _vcKey: VCMetadata.fromVC(context.vcMetadata).getVcKey(), - type: 'WALLET_BINDING_SUCCESSFULL', - idType: getIdType(context.vcMetadata.issuer), - id: context.vcMetadata.id, - timestamp: Date.now(), - deviceName: '', - vcLabel: VCMetadata.fromVC(context.vcMetadata).id, - }), - { - to: context => context.serviceRefs.activityLog, - }, - ), - - logWalletBindingFailure: send( - context => - ActivityLogEvents.LOG_ACTIVITY({ - _vcKey: VCMetadata.fromVC(context.vcMetadata).getVcKey(), - type: 'WALLET_BINDING_FAILURE', - id: context.vcMetadata.id, - idType: getIdType(context.vcMetadata.issuer), - timestamp: Date.now(), - deviceName: '', - vcLabel: VCMetadata.fromVC(context.vcMetadata).id, - }), - { - to: context => context.serviceRefs.activityLog, - }, - ), - setOtp: model.assign({ - otp: (_, event) => event.otp, - }), - - setOtpError: assign({ - otpError: (_context, event) => - (event as ErrorPlatformEvent).data.message, - }), - - setPhoneNumber: assign({ - phoneNumber: (_context, event) => event.data.response.maskedMobile, - }), - - setEmail: model.assign({ - email: (_context, event) => event.data.response.maskedEmail, - }), - - clearOtp: assign({otp: ''}), - removeVcItem: send( - _context => { - return StoreEvents.REMOVE( - MY_VCS_STORE_KEY, - _context.vcMetadata.getVcKey(), - ); - }, - {to: context => context.serviceRefs.store}, - ), - - closeViewVcModal: send('DISMISS_MODAL', { - to: () => getHomeMachineService(), - }), - - setVcKey: model.assign({ - vcMetadata: (_, event) => event.vcMetadata, - }), - logVCremoved: send( - (context, _) => - ActivityLogEvents.LOG_ACTIVITY({ - idType: getIdType(context.vcMetadata.issuer), - id: context.vcMetadata.id, - _vcKey: VCMetadata.fromVC(context.vcMetadata).getVcKey(), - type: 'VC_REMOVED', - timestamp: Date.now(), - deviceName: '', - vcLabel: VCMetadata.fromVC(context.vcMetadata).id, - }), - { - to: context => context.serviceRefs.activityLog, - }, - ), - }, - - services: { - isUserSignedAlready: () => async () => { - return await Cloud.isSignedInAlready(); - }, - - updatePrivateKey: async context => { - const hasSetPrivateKey: boolean = await savePrivateKey( - context.tempWalletBindingIdResponse.walletBindingId, - context.privateKey, - ); - if (!hasSetPrivateKey) { - throw new Error('Could not store private key in keystore.'); - } - return ''; - }, - addWalletBindnigId: async context => { - const response = await request( - API_URLS.walletBinding.method, - API_URLS.walletBinding.buildURL(), - { - requestTime: String(new Date().toISOString()), - request: { - authFactorType: 'WLA', - format: 'jwt', - individualId: VCMetadata.fromVC(context.vcMetadata).id, - transactionId: context.transactionId, - publicKey: context.publicKey, - challengeList: [ - { - authFactorType: 'OTP', - challenge: context.otp, - format: 'alpha-numeric', - }, - ], - }, - }, - ); - const certificate = response.response.certificate; - await savePrivateKey( - getBindingCertificateConstant( - VCMetadata.fromVC(context.vcMetadata).id, - ), - certificate, - ); - - const walletResponse: WalletBindingResponse = { - walletBindingId: response.response.encryptedWalletBindingId, - keyId: response.response.keyId, - thumbprint: response.response.thumbprint, - expireDateTime: response.response.expireDateTime, - }; - return walletResponse; - }, - generateKeyPair: async context => { - if (!isHardwareKeystoreExists) { - return await generateKeys(); - } - const isBiometricsEnabled = SecureKeystore.hasBiometricsEnabled(); - return SecureKeystore.generateKeyPair( - VCMetadata.fromVC(context.vcMetadata).id, - isBiometricsEnabled, - 0, - ); - }, - requestBindingOtp: async context => { - const response = await request( - API_URLS.bindingOtp.method, - API_URLS.bindingOtp.buildURL(), - { - requestTime: String(new Date().toISOString()), - request: { - individualId: getMosipIdentifier( - context.verifiableCredential.credential.credentialSubject, - ), - otpChannels: ['EMAIL', 'PHONE'], - }, - }, - ); - if (response.response == null) { - throw new Error('Could not process request'); - } - return response; - }, - }, - - guards: { - hasCredential: (_, event) => { - const vc = event.vc; - return vc != null; - }, - isSignedIn: (_context, event) => - (event.data as isSignedInResult).isSignedIn, - - isCustomSecureKeystore: () => isHardwareKeystoreExists, - }, - }, -); - -export const createEsignetMosipVCItemMachine = ( - serviceRefs: AppServices, - vcMetadata: VCMetadata, -) => { - return EsignetMosipVCItemMachine.withContext({ - ...EsignetMosipVCItemMachine.context, - serviceRefs, - vcMetadata, - }); -}; diff --git a/machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine.typegen.ts b/machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine.typegen.ts deleted file mode 100644 index c134a865..00000000 --- a/machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine.typegen.ts +++ /dev/null @@ -1,184 +0,0 @@ -// This file was automatically generated. Edits will be overwritten - -export interface Typegen0 { - '@@xstate/typegen': true; - internalEvents: { - 'done.invoke.vc-item-openid4vci.addKeyPair:invocation[0]': { - type: 'done.invoke.vc-item-openid4vci.addKeyPair:invocation[0]'; - data: unknown; - __tip: 'See the XState TS docs to learn how to strongly type this.'; - }; - 'done.invoke.vc-item-openid4vci.addingWalletBindingId:invocation[0]': { - type: 'done.invoke.vc-item-openid4vci.addingWalletBindingId:invocation[0]'; - data: unknown; - __tip: 'See the XState TS docs to learn how to strongly type this.'; - }; - 'done.invoke.vc-item-openid4vci.kebabPopUp.triggerAutoBackup:invocation[0]': { - type: 'done.invoke.vc-item-openid4vci.kebabPopUp.triggerAutoBackup:invocation[0]'; - data: unknown; - __tip: 'See the XState TS docs to learn how to strongly type this.'; - }; - 'done.invoke.vc-item-openid4vci.requestingBindingOtp:invocation[0]': { - type: 'done.invoke.vc-item-openid4vci.requestingBindingOtp:invocation[0]'; - data: unknown; - __tip: 'See the XState TS docs to learn how to strongly type this.'; - }; - 'done.invoke.vc-item-openid4vci.updatingPrivateKey:invocation[0]': { - type: 'done.invoke.vc-item-openid4vci.updatingPrivateKey:invocation[0]'; - data: unknown; - __tip: 'See the XState TS docs to learn how to strongly type this.'; - }; - 'error.platform.vc-item-openid4vci.acceptingBindingOtp.resendOTP:invocation[0]': { - type: 'error.platform.vc-item-openid4vci.acceptingBindingOtp.resendOTP:invocation[0]'; - data: unknown; - }; - 'error.platform.vc-item-openid4vci.addKeyPair:invocation[0]': { - type: 'error.platform.vc-item-openid4vci.addKeyPair:invocation[0]'; - data: unknown; - }; - 'error.platform.vc-item-openid4vci.addingWalletBindingId:invocation[0]': { - type: 'error.platform.vc-item-openid4vci.addingWalletBindingId:invocation[0]'; - data: unknown; - }; - 'error.platform.vc-item-openid4vci.requestingBindingOtp:invocation[0]': { - type: 'error.platform.vc-item-openid4vci.requestingBindingOtp:invocation[0]'; - data: unknown; - }; - 'error.platform.vc-item-openid4vci.updatingPrivateKey:invocation[0]': { - type: 'error.platform.vc-item-openid4vci.updatingPrivateKey:invocation[0]'; - data: unknown; - }; - 'xstate.init': {type: 'xstate.init'}; - }; - invokeSrcNameMap: { - addWalletBindnigId: 'done.invoke.vc-item-openid4vci.addingWalletBindingId:invocation[0]'; - generateKeyPair: 'done.invoke.vc-item-openid4vci.addKeyPair:invocation[0]'; - isUserSignedAlready: 'done.invoke.vc-item-openid4vci.kebabPopUp.triggerAutoBackup:invocation[0]'; - requestBindingOtp: - | 'done.invoke.vc-item-openid4vci.acceptingBindingOtp.resendOTP:invocation[0]' - | 'done.invoke.vc-item-openid4vci.requestingBindingOtp:invocation[0]'; - updatePrivateKey: 'done.invoke.vc-item-openid4vci.updatingPrivateKey:invocation[0]'; - }; - missingImplementations: { - actions: 'clearTransactionId'; - delays: never; - guards: never; - services: never; - }; - eventsCausingActions: { - clearOtp: - | 'DISMISS' - | 'done.invoke.vc-item-openid4vci.requestingBindingOtp:invocation[0]'; - clearTransactionId: 'DISMISS'; - closeViewVcModal: 'CLOSE_VC_MODAL' | 'STORE_RESPONSE'; - logVCremoved: - | 'STORE_RESPONSE' - | 'done.invoke.vc-item-openid4vci.kebabPopUp.triggerAutoBackup:invocation[0]'; - logWalletBindingFailure: - | 'error.platform.vc-item-openid4vci.addKeyPair:invocation[0]' - | 'error.platform.vc-item-openid4vci.addingWalletBindingId:invocation[0]' - | 'error.platform.vc-item-openid4vci.requestingBindingOtp:invocation[0]' - | 'error.platform.vc-item-openid4vci.updatingPrivateKey:invocation[0]'; - logWalletBindingSuccess: - | 'done.invoke.vc-item-openid4vci.addingWalletBindingId:invocation[0]' - | 'done.invoke.vc-item-openid4vci.updatingPrivateKey:invocation[0]'; - removeVcItem: 'CONFIRM'; - removedVc: - | 'STORE_RESPONSE' - | 'done.invoke.vc-item-openid4vci.kebabPopUp.triggerAutoBackup:invocation[0]'; - requestStoredContext: 'GET_VC_RESPONSE' | 'REFRESH'; - requestVcContext: 'DISMISS' | 'xstate.init'; - sendActivationFailedEndEvent: - | 'DISMISS' - | 'error.platform.vc-item-openid4vci.updatingPrivateKey:invocation[0]'; - sendActivationStartEvent: 'CONFIRM'; - sendActivationSuccessEvent: - | 'done.invoke.vc-item-openid4vci.addingWalletBindingId:invocation[0]' - | 'done.invoke.vc-item-openid4vci.updatingPrivateKey:invocation[0]'; - sendBackupEvent: 'done.invoke.vc-item-openid4vci.kebabPopUp.triggerAutoBackup:invocation[0]'; - sendVcUpdated: 'PIN_CARD'; - sendWalletBindingSuccess: 'SHOW_BINDING_STATUS'; - setContext: 'GET_VC_RESPONSE' | 'STORE_RESPONSE'; - setGeneratedOn: 'GET_VC_RESPONSE'; - setOtp: 'INPUT_OTP'; - setPinCard: 'PIN_CARD'; - setPrivateKey: 'done.invoke.vc-item-openid4vci.addKeyPair:invocation[0]'; - setPublicKey: 'done.invoke.vc-item-openid4vci.addKeyPair:invocation[0]'; - setTempWalletBindingResponse: 'done.invoke.vc-item-openid4vci.addingWalletBindingId:invocation[0]'; - setThumbprintForWalletBindingId: - | 'done.invoke.vc-item-openid4vci.addingWalletBindingId:invocation[0]' - | 'done.invoke.vc-item-openid4vci.updatingPrivateKey:invocation[0]'; - setVcKey: 'REMOVE'; - setVcMetadata: 'UPDATE_VC_METADATA'; - setVerifiableCredential: 'GET_VC_RESPONSE' | 'STORE_RESPONSE'; - setWalletBindingError: - | 'error.platform.vc-item-openid4vci.acceptingBindingOtp.resendOTP:invocation[0]' - | 'error.platform.vc-item-openid4vci.addKeyPair:invocation[0]' - | 'error.platform.vc-item-openid4vci.addingWalletBindingId:invocation[0]' - | 'error.platform.vc-item-openid4vci.requestingBindingOtp:invocation[0]' - | 'error.platform.vc-item-openid4vci.updatingPrivateKey:invocation[0]'; - setWalletBindingErrorEmpty: - | 'CANCEL' - | 'done.invoke.vc-item-openid4vci.addingWalletBindingId:invocation[0]' - | 'done.invoke.vc-item-openid4vci.updatingPrivateKey:invocation[0]'; - setWalletBindingId: - | 'done.invoke.vc-item-openid4vci.addingWalletBindingId:invocation[0]' - | 'done.invoke.vc-item-openid4vci.updatingPrivateKey:invocation[0]'; - storeContext: - | 'done.invoke.vc-item-openid4vci.addingWalletBindingId:invocation[0]' - | 'done.invoke.vc-item-openid4vci.updatingPrivateKey:invocation[0]'; - updatePrivateKey: - | 'done.invoke.vc-item-openid4vci.addingWalletBindingId:invocation[0]' - | 'done.invoke.vc-item-openid4vci.updatingPrivateKey:invocation[0]'; - updateVc: - | 'STORE_RESPONSE' - | 'done.invoke.vc-item-openid4vci.addingWalletBindingId:invocation[0]' - | 'done.invoke.vc-item-openid4vci.updatingPrivateKey:invocation[0]'; - }; - eventsCausingDelays: {}; - eventsCausingGuards: { - hasCredential: 'GET_VC_RESPONSE'; - isCustomSecureKeystore: - | 'done.invoke.vc-item-openid4vci.addKeyPair:invocation[0]' - | 'done.invoke.vc-item-openid4vci.addingWalletBindingId:invocation[0]'; - isSignedIn: 'done.invoke.vc-item-openid4vci.kebabPopUp.triggerAutoBackup:invocation[0]'; - }; - eventsCausingServices: { - addWalletBindnigId: 'done.invoke.vc-item-openid4vci.addKeyPair:invocation[0]'; - generateKeyPair: 'INPUT_OTP'; - isUserSignedAlready: 'STORE_RESPONSE'; - requestBindingOtp: 'CONFIRM' | 'RESEND_OTP'; - updatePrivateKey: 'done.invoke.vc-item-openid4vci.addingWalletBindingId:invocation[0]'; - }; - matchesStates: - | 'acceptingBindingOtp' - | 'acceptingBindingOtp.idle' - | 'acceptingBindingOtp.resendOTP' - | 'addKeyPair' - | 'addingWalletBindingId' - | 'checkingStore' - | 'checkingVc' - | 'idle' - | 'kebabPopUp' - | 'kebabPopUp.idle' - | 'kebabPopUp.removeWallet' - | 'kebabPopUp.removingVc' - | 'kebabPopUp.showActivities' - | 'kebabPopUp.triggerAutoBackup' - | 'pinCard' - | 'requestingBindingOtp' - | 'showBindingWarning' - | 'showingWalletBindingError' - | 'updatingContextVariables' - | 'updatingPrivateKey' - | { - acceptingBindingOtp?: 'idle' | 'resendOTP'; - kebabPopUp?: - | 'idle' - | 'removeWallet' - | 'removingVc' - | 'showActivities' - | 'triggerAutoBackup'; - }; - tags: never; -} diff --git a/machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine.ts b/machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine.ts deleted file mode 100644 index a9f2c5d3..00000000 --- a/machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine.ts +++ /dev/null @@ -1,1312 +0,0 @@ -import {assign, ErrorPlatformEvent, EventFrom, send, StateFrom} from 'xstate'; -import {createModel} from 'xstate/lib/model'; -import {MIMOTO_BASE_URL, MY_VCS_STORE_KEY} from '../../../shared/constants'; -import {AppServices} from '../../../shared/GlobalContext'; -import {CredentialDownloadResponse, request} from '../../../shared/request'; -import { - DecodedCredential, - VC, - VcIdType, - VerifiableCredential, -} from '../../../types/VC/ExistingMosipVC/vc'; -import {StoreEvents} from '../../store'; -import {ActivityLogEvents} from '../../activityLog'; -import {verifyCredential} from '../../../shared/vcjs/verifyCredential'; -import {log} from 'xstate/lib/actions'; -import { - generateKeys, - isHardwareKeystoreExists, - WalletBindingResponse, -} from '../../../shared/cryptoutil/cryptoUtil'; -import {KeyPair} from 'react-native-rsa-native'; -import { - getBindingCertificateConstant, - savePrivateKey, -} from '../../../shared/keystore/SecureKeystore'; -import getAllConfigurations, {DownloadProps} from '../../../shared/api'; -import {VcEvents} from '../vc'; -import i18n from '../../../i18n'; -import SecureKeystore from '@mosip/secure-keystore'; -import {VCMetadata} from '../../../shared/VCMetadata'; -import { - getEndEventData, - getStartEventData, - sendEndEvent, - sendInteractEvent, - getInteractEventData, - sendStartEvent, -} from '../../../shared/telemetry/TelemetryUtils'; -import {TelemetryConstants} from '../../../shared/telemetry/TelemetryConstants'; - -import {API_URLS} from '../../../shared/api'; -import {getHomeMachineService} from '../../../screens/Home/HomeScreenController'; -import {BackupEvents} from '../../backupAndRestore/backup'; -import Cloud, { - isSignedInResult, -} from '../../../shared/CloudBackupAndRestoreUtils'; -import {getIdType} from '../../../shared/openId4VCI/Utils'; - -const model = createModel( - { - serviceRefs: {} as AppServices, - id: '', - idType: '' as VcIdType, - vcMetadata: {} as VCMetadata, - myVcs: [] as string[], - generatedOn: new Date() as Date, - credential: null as DecodedCredential, - verifiableCredential: null as VerifiableCredential, - requestId: '', - lastVerifiedOn: null, - otp: '', - otpError: '', - idError: '', - transactionId: '', - bindingTransactionId: '', - downloadCounter: 0, - maxDownloadCount: null as number, - downloadInterval: null as number, - walletBindingResponse: null as WalletBindingResponse, - tempWalletBindingIdResponse: null as WalletBindingResponse, - walletBindingError: '', - publicKey: '', - privateKey: '', - isMachineInKebabPopupState: false, - bindingAuthFailedMessage: '' as string, - verificationErrorMessage: '', - phoneNumber: '' as string, - email: '' as string, - }, - { - events: { - KEY_RECEIVED: (key: string) => ({key}), - KEY_ERROR: (error: Error) => ({error}), - STORE_READY: () => ({}), - DISMISS: () => ({}), - CREDENTIAL_DOWNLOADED: (vc: VC) => ({vc}), - STORE_RESPONSE: (response: VC) => ({response}), - POLL: () => ({}), - DOWNLOAD_READY: () => ({}), - FAILED: () => ({}), - GET_VC_RESPONSE: (vc: VC) => ({vc}), - VERIFY: () => ({}), - INPUT_OTP: (otp: string) => ({otp}), - RESEND_OTP: () => ({}), - REFRESH: () => ({}), - ADD_WALLET_BINDING_ID: () => ({}), - CANCEL: () => ({}), - CONFIRM: () => ({}), - STORE_ERROR: (error: Error) => ({error}), - PIN_CARD: () => ({}), - KEBAB_POPUP: () => ({}), - SHOW_ACTIVITY: () => ({}), - CLOSE_VC_MODAL: () => ({}), - REMOVE: (vcMetadata: VCMetadata) => ({vcMetadata}), - UPDATE_VC_METADATA: (vcMetadata: VCMetadata) => ({vcMetadata}), - TAMPERED_VC: (key: string) => ({key}), - SHOW_BINDING_STATUS: () => ({}), - }, - }, -); - -export const ExistingMosipVCItemEvents = model.events; - -export const ExistingMosipVCItemMachine = - /** @xstate-layout N4IgpgJg5mDOIC5QDcDGBaAlgFzAWwGIAlAUQDFSBlACQG0AGAXUVAAcB7WHTdgOxZAAPRABYATABoQAT0QBGABwBWAHT0FAZjEB2bSKUBOA9rliAvmalosuPCtQALMKgDWmXlABqqAgHESACoA+p4AwkFUAAoA8gBylCQMzEggHFzYPPwpwggKBiIqcvqaAGwGSvT0SnJSsgjicio69Np5GvTFSiIWVhg4+PZOru5ePv7BYREklDHxiXLJbJzcfAI5eQVFSqXlldW1iO2qmlpKhtpnGkbdliDW-XaOzm4elNjsAE5gBJQB0aRTGZxBJJARpFZZUA5OSmA4IMRyDQFDRKDQmMRiDRyAwKG69GwDJ7DV7vL4-P4AqLA+aLVLLDKrbLyJRiBRNAxybT0MRnQwKBSSGTyXFNERGPIwhFo-k9O59WyDZ4jShgD7IVUAEQAhtgtYriVA3jqAK6wAgxAAyFtBKXBDMhQkQPO0TTORQMJRZ2hKyjhihEbLkHRaXVRcmqCll9wVRJehtV6o+2t1+rjRuwpoIGuiAHVYhbogBBDVTYsATRtS3SmTWokMhWD9HyJRKRUxfuMYjUJQRBnaii6VSj8sJQzTCc1Or1EHYAHdeAAbdhaiAjUJfCBgXgZLUL83RK2VunVxlQxAKIMqETiCVo6pnBR+hQlDSFBTaMQlEQdL8dPFyglHjHZUJyTKcVBneclxXNcNy3Hc91CUgNRIWIAgASULC0gmzPMC2LEgNSPO0ayZBAg3yQoURMGjLg9DsxAKD88gxERsQDVph0AlRMAgBdvkI9DggCQtfGI+lSLPeERBdNiShMIMtDFb8lDhT9ORUNERDKZ9rw-JQuIeHi+O+TwSCIdCyArJgwQk09HXhT8ShUd8ujyFkMUMEo1LEFoVBZbl6BhBQQzRQyFV4-iCALUIAGkQlCcSTwdHIdA0V8kW01k5BKegexaNTvUad9r206of3MW5owGSLvlITxolikgEqSiFa1yQU6lZEKXL0JRPS5AMXwMqqRzsWqCGLEscywi1AiCAAhdDYg1ZbfCCdCiJs207JS+ROv28KBkgbgPACLUoCzdDKAAWWuyhWvtdrMVknKFPoJT8iqNT0voQp9By79TB7AwjrsE6MjOi6fkLMyghEsTtqrNqyMY175JhD7GK+1ShWk6pChbREBQMDpPzBlRYFJEZzsu35-maqk5keySHP645yhbDolMUbQ1IBwnWw0EmyZKCnE0wAAzaRYMgeDMF3AgZ14MAeN4ZB2BcVXqrsCXpdlzdtwVhcEHcDXUB1TIkhZ+zUqc3q3PKDEeXovHfNbFQvwqSoUSbF9xdVKWZY8dc5aNxXVQ+T4VFYBcdUlz5dbGlQ9eDqBQ8NhDTfV9gLfta2kePFGpM85zXIDJ3PNdupsR9T3n3KFFlDYnKKdjEZPEDyXMDzzJ00zG29vI5QDBc5QuSMfqPUUJ8KjfMogp5apurb4CPE7j4pZ7y2+H7s1aAWWzkvagdR-5JQJ856fHzdxRR69gMZP5FptAps3d14ghlsiABVYJogCJEQe7UtAujyjyHQRR5JiD7HCEQVxVBP2MHlPq-tRrcXfguT+q1br3WAWRUB3YIEmG0h+WBeNhZflFMYVsYoSGVXxEZL4ABHY0cBIZQGiNgVgSs+CqzNprbWycWFsKpiMLhrBs7mx3rwAutISK23kDCV8MDfLpX6pUD6cCuTOT7NoShbEL75ApiI9h4juEEEjtHWO8dE4p2EWAVhZiPASKkbnGRcij7FwcjCRE7I1Eolyj7OBQsVDGFJp6fQMCigUy1KgVAYBWAcIkehXgrBjTYC-rEX+-9AH4KkmUVQ4C3S4lKTiOBzQVDMQFEDC+SJYnxMSck7hqT0mZJwXdSgD1C4KKHqYJs9cQrtHKHoRicINAY38iTTQ1waIaAaQkpJIwiBgA1lrVpGSsk5KCAAoBPTdonzeoUIov1tBfRgbjGuiJjjpS0O0TQLIRqMIVHExZHCVlrLABs9p11OndPkQcsiRhGimH0GKAUXIsZwjIVebYPIfSIkfq-dBTDHGiI4RaXOLheEqzVp8+x3FTFiI8Ji1wbje58E8TtY+ZEfRFJ7CUx+eQb51CuM+KpXJzj3MUGLFFCoiUYqxZYj4UcPgxzjtgBOHwk6ErRc4qApKXDko8UwfJDlClEMZWUllog2L3w+n2KUDyhx8oGEuA03hyQM0BLMEE+yaVSV8SogwASNHBLxt+PyRgPTvhyuoF1Ji5XEqgB8wROL+E5y1gS1FTjg2hq1sq-Oqr7XeNSqTJo6g0TekxBeE54yfaC17PJF86hA2xveassNVixU2MlXYnWKgBXLMrQmgRFLZHJoBQ6hyhDimQNITAjQcCYSqACmKPKTt0qBrWR3Hw9NKTTFtYkFNT1aVnJcuUAUFQrifj0PmtEntGKtmbPJb8vLnlmvYFAKAzbPlXVwV0tV0IX5Xh9N6xEaJWQHQQO0A9PZtLsRbHoPKZb0UjAWu4VcLiLHKwjfihtTaPAQd4FBzh3DE1W07V41dUlf1hMgQ8oMRjv3A1UEGM9T8ijiCeQBGNYGkOQfMTw6t4rbHSujfyoNHDkOodcW2lVjAn2HA+vhkwhGqhnO-RURogVi1C36vAimsAHBzhICKz4BBQiFliKEEg1oV2sxyJ6ZypgKjPm9HkPKOqEAeWod6eBjFFJyAWU08DjHoM8O-n-HZeSDOKIQBcWSrQZk5UoeGP0S8qnRPDF+i4wsXNLIYyhpj96-lCYCxfK8wWrihefOFvGF8ux6G9byZ0sSIAQFimAaQkQtSYA+OGvFgiOMDBXJV6rtX6sYcpVh6lqalHyWOfA9oZzvwXIiyKLkJM2ItBkvo8r7Wat1Yayx2tUqZVGTa1VpbXX+NJsE35vpRyYTDZaOc8ofoya9TFIDdK4TkUXrsG1kYOZdz8WwDxkY6EICNYEVGhtz2PCvYXO9z7HhvvdY7Qdrt-Wf0aT7SQ6B5DWWFc0noeS6V9H9Xqaap7FWXtvbAB99zUBvvCtFaxut7GAf46B4T4nyXwcQEh1S5GOGe1+QR1AshQ6KFYk2IxVEAomwohow240rAICWw8JETeyAdRgG279yNQjuIS6lxw2XmB5e4G2yz3rbPDOIFaGRlETKZKYhfOMupmlyiDkChcEDuOVDq+l1ALXOvFfVfJ9YiV62Wt2Fd5ruXCu9d7cw9D7DRuAvKCom5JFlveesv0CZj6ehwxIlMA92jCplNzjB4aXUGYzRLRWmtHCcRl0w-Z9CXy99n7DIvuIEQ4yewyZJhMowPIQojVuLwdgm54ApB1lH-z6AJlwnQP0tQmjTBIgFFnhhOfRxKnXqgUfQ9bPexyuUD0YpDDjNj8V5sQUX4nFXqvwvnwwAb5PgiAomi-YIlZMUfmzlj-FFaF+zEF+DQqjVJOLqLfmRCdn6GUM5LlN+M+O0PJE7o9qmCBAAWBCmO3CSCaEPobv5jlCRvkKPOAr5OoKUoYGLsnKgfGEgcmNOHOIuMuKhhnPLLuMAQUnXEiFmnUqiLoDgVQlsMLPkK0L4uesvuNCZEwQ5NjA-pKKiEYPoroIVDyNdkNPkCOq3M7hDDTBdKIc+t+p+AMmKMYFiO+B6KDM7lTJ8OoVAJoYcLHuEkFMLJ+FiC6t5G7PoH9O5DoB6J+M+BiAHJvPrCHHBOHAuJYfCNyEVtiBiKwb5J+GpIDLbl0F+kYBMkvg2mQRvFvO2nvMEYiINufDRB+BZloDEcLC5KYHXopNsLiG-OrB-BAMEWxFeO+pUDApUB+nAkiH9MUHlNiPoiFMkcnJgrxCoOwNwsEXkA0cCk0aTLYUnrqjoGEtsFnl7MVlUfLlghACoAAEZxJawobBEupwJDSwpnJ8FVD8jZ4IZcZMbBGshUK5QsgTJBQXgi4hIDJTYIjchGB3EJbNKsDfKjHrpfhCzwJ5b6EVKaBhKhYuq6R0IkHcSvKuYeDxpfJpIZLBGBZZb8g5Yvh5aXJKKVBxE95iY3aRjO6IYKpYp1EfiFA8iIichcgnbfrwLlAZq5q+SYxBQUzmpxjeD-F-ThIyQfQwh6B5DjI4gFDeqFLcgfgwiCEXHlq3qCLBHyTDqmBRYN475jbTqayzp1EcgEl6IBjVC8z5pFCHr6BohJF5QdCclXo3qIkto359Y17njrriBiYupOxVC4k-otFmn9S6C4ifioigbyoF4SJ1EkaMR-RTZojTynHGImEqazhqairBFej+QtGFJ7675wi2Yf6-gCjySwmbaNKJZQBhkjFOnR4shsgnCmDKCVAmDlJ4ymZ-S3LNDBiwHGHwFbYdbLZ7FIiaRC6fh0Kfj7EtnZQQmejKDhjigmo9m05QDA6g4k7fZpl8weoQLzEL55TaTzLO5B4jAe6h7VZpn6ANh6ruHwIyTelNyvgnBVAejqBfgknwF56zgF6ZFVlYEFpQKKQchvStDW44hqDTaGl5AWAWBAA */ - model.createMachine( - { - predictableActionArguments: true, - preserveActionOrder: true, - tsTypes: {} as import('./ExistingMosipVCItemMachine.typegen').Typegen0, - schema: { - context: model.initialContext, - events: {} as EventFrom, - }, - on: { - REFRESH: { - target: '.checkingStore', - }, - UPDATE_VC_METADATA: { - actions: 'setVcMetadata', - }, - }, - description: 'VC', - id: 'vc-item', - initial: 'checkingVc', - states: { - checkingVc: { - entry: 'requestVcContext', - description: - 'Check if the VC data is in VC list already (in memory).', - on: { - GET_VC_RESPONSE: [ - { - actions: 'setCredential', - cond: 'hasCredential', - target: '#vc-item.idle', - }, - { - target: 'checkingStore', - }, - ], - }, - }, - checkingStore: { - entry: 'requestStoredContext', - description: 'Check if VC data is in secured local storage.', - on: { - STORE_RESPONSE: [ - { - actions: ['setCredential', 'updateVc'], - cond: 'hasCredential', - target: '#vc-item.idle', - }, - { - actions: 'addVcToInProgressDownloads', - target: 'checkingServerData', - }, - ], - TAMPERED_VC: { - actions: 'sendTamperedVc', - }, - }, - }, - checkingServerData: { - description: - "Download VC data from the server. Uses polling method to check when it's available.", - initial: 'loadDownloadLimitConfig', - states: { - loadDownloadLimitConfig: { - invoke: { - src: 'loadDownloadLimitConfig', - onDone: { - actions: ['setMaxDownloadCount', 'setDownloadInterval'], - target: 'verifyingDownloadLimitExpiry', - }, - }, - }, - verifyingDownloadLimitExpiry: { - entry: ['incrementDownloadCounter'], - invoke: { - src: 'checkDownloadExpiryLimit', - onDone: { - target: 'checkingStatus', - }, - onError: { - actions: [ - log((_, event) => (event.data as Error).message), - 'sendDownloadLimitExpire', - ], - }, - }, - }, - checkingStatus: { - invoke: { - src: 'checkStatus', - id: 'checkStatus', - }, - on: { - POLL: { - cond: 'isDownloadAllowed', - actions: send('POLL_STATUS', {to: 'checkStatus'}), - }, - DOWNLOAD_READY: { - target: 'downloadingCredential', - }, - FAILED: { - actions: 'sendDownloadLimitExpire', - }, - }, - }, - downloadingCredential: { - invoke: { - src: 'downloadCredential', - id: 'downloadCredential', - }, - on: { - POLL: [ - { - cond: 'isDownloadAllowed', - actions: [ - send('POLL_DOWNLOAD', {to: 'downloadCredential'}), - 'incrementDownloadCounter', - ], - }, - { - target: 'verifyingDownloadLimitExpiry', - }, - ], - CREDENTIAL_DOWNLOADED: { - actions: 'setCredential', - target: '#vc-item.verifyingCredential', - }, - }, - }, - savingFailed: { - entry: ['removeVcMetaDataFromStorage'], - initial: 'idle', - states: { - idle: {}, - viewingVc: {}, - }, - on: { - DISMISS: { - actions: ['removeVcMetaDataFromVcMachine'], - target: '.viewingVc', - }, - }, - }, - }, - }, - idle: { - entry: ['clearTransactionId', 'clearOtp'], - on: { - VERIFY: { - target: 'verifyingCredential', - }, - ADD_WALLET_BINDING_ID: { - target: 'showBindingWarning', - }, - PIN_CARD: { - target: 'pinCard', - actions: 'setPinCard', - }, - KEBAB_POPUP: { - target: 'kebabPopUp', - }, - DISMISS: { - target: 'checkingVc', - }, - }, - }, - pinCard: { - entry: 'sendVcUpdated', - always: { - target: 'idle', - }, - }, - kebabPopUp: { - entry: assign({isMachineInKebabPopupState: () => true}), - exit: assign({isMachineInKebabPopupState: () => false}), - on: { - DISMISS: { - actions: assign({ - isMachineInKebabPopupState: () => false, - }), - target: 'idle', - }, - ADD_WALLET_BINDING_ID: { - target: '#vc-item.showBindingWarning', - }, - PIN_CARD: { - target: '#vc-item.pinCard', - actions: 'setPinCard', - }, - SHOW_ACTIVITY: { - target: '#vc-item.kebabPopUp.showActivities', - }, - REMOVE: { - actions: 'setVcKey', - target: '#vc-item.kebabPopUp.removeWallet', - }, - CLOSE_VC_MODAL: { - actions: ['closeViewVcModal'], - target: '#vc-item', - }, - }, - initial: 'idle', - states: { - idle: {}, - showActivities: { - on: { - DISMISS: '#vc-item', - }, - }, - removeWallet: { - on: { - CONFIRM: { - target: 'removingVc', - }, - CANCEL: { - target: '#vc-item', - }, - }, - }, - removingVc: { - entry: 'removeVcItem', - on: { - STORE_RESPONSE: { - actions: ['closeViewVcModal', 'refreshMyVcs', 'logVCremoved'], - target: '.triggerAutoBackup', - }, - }, - states: { - triggerAutoBackup: { - invoke: { - src: 'isUserSignedAlready', - onDone: [ - { - cond: 'isSignedIn', - actions: [ - 'sendBackupEvent', - 'refreshMyVcs', - 'logVCremoved', - ], - target: '#vc-item', - }, - { - actions: ['refreshMyVcs', 'logVCremoved'], - target: '#vc-item', - }, - ], - }, - }, - }, - }, - }, - }, - verifyingCredential: { - invoke: { - src: 'verifyCredential', - onDone: [ - { - actions: ['storeContext'], - }, - ], - onError: [ - { - //To-Do Handle Error Scenarios - actions: ['updateVerificationErrorMessage'], - target: 'handleVCVerificationFailure', - }, - ], - }, - initial: 'idle', - on: { - STORE_RESPONSE: { - actions: [ - 'updateVc', - 'logDownloaded', - 'sendTelemetryEvents', - 'removeVcFromInProgressDownloads', - ], - target: '.triggerAutoBackupForVcDownload', - }, - STORE_ERROR: { - target: '#vc-item.checkingServerData.savingFailed', - }, - }, - states: { - idle: {}, - triggerAutoBackupForVcDownload: { - invoke: { - src: 'isUserSignedAlready', - onDone: [ - { - cond: 'isSignedIn', - actions: ['sendBackupEvent'], - target: '#vc-item.idle', - }, - { - target: '#vc-item.idle', - }, - ], - }, - }, - }, - }, - - handleVCVerificationFailure: { - entry: ['removeVcMetaDataFromStorage'], - on: { - STORE_RESPONSE: { - actions: ['sendVerificationError'], - }, - }, - }, - showBindingWarning: { - on: { - CONFIRM: { - actions: 'sendActivationStartEvent', - target: 'requestingBindingOtp', - }, - CANCEL: [ - { - cond: context => context.isMachineInKebabPopupState, - target: '#vc-item.kebabPopUp', - }, - { - target: 'idle', - }, - ], - }, - }, - requestingBindingOtp: { - invoke: { - src: 'requestBindingOtp', - onDone: [ - { - target: 'acceptingBindingOtp', - actions: ['setPhoneNumber', 'setEmail'], - }, - ], - onError: [ - { - actions: ['setWalletBindingError', 'logWalletBindingFailure'], - target: 'showingWalletBindingError', - }, - ], - }, - }, - showingWalletBindingError: { - on: { - CANCEL: [ - { - cond: context => context.isMachineInKebabPopupState, - actions: ['setWalletBindingErrorEmpty'], - target: '#vc-item.kebabPopUp', - }, - { - actions: ['setWalletBindingErrorEmpty'], - target: 'idle', - }, - ], - }, - }, - acceptingBindingOtp: { - entry: ['clearOtp'], - on: { - INPUT_OTP: { - target: 'addKeyPair', - actions: ['setOtp'], - }, - DISMISS: [ - { - cond: context => context.isMachineInKebabPopupState, - target: '#vc-item.kebabPopUp', - actions: [ - 'sendActivationFailedEndEvent', - 'clearOtp', - 'clearTransactionId', - ], - }, - { - target: 'idle', - actions: [ - 'sendActivationFailedEndEvent', - 'clearOtp', - 'clearTransactionId', - ], - }, - ], - RESEND_OTP: { - target: '.resendOTP', - }, - }, - initial: 'idle', - states: { - idle: {}, - resendOTP: { - invoke: { - src: 'requestBindingOtp', - onDone: { - target: 'idle', - actions: ['setPhoneNumber', 'setEmail'], - }, - onError: { - actions: 'setWalletBindingError', - target: '#vc-item.showingWalletBindingError', - }, - }, - }, - }, - }, - addKeyPair: { - invoke: { - src: 'generateKeyPair', - onDone: [ - { - cond: 'isCustomSecureKeystore', - target: 'addingWalletBindingId', - actions: ['setPublicKey'], - }, - { - target: 'addingWalletBindingId', - actions: ['setPublicKey', 'setPrivateKey'], - }, - ], - onError: [ - { - actions: ['setWalletBindingError', 'logWalletBindingFailure'], - target: 'showingWalletBindingError', - }, - ], - }, - }, - addingWalletBindingId: { - invoke: { - src: 'addWalletBindnigId', - onDone: [ - { - cond: 'isCustomSecureKeystore', - target: 'updatingContextVariables', - }, - { - target: 'updatingPrivateKey', - /*The walletBindingResponse is used for conditional rendering in wallet binding. - However, it wrongly considers activation as successful even when there's an error - in updatingPrivateKey state. So created a temporary context variable to store the binding - response and use it in updatingPrivateKey state*/ - actions: 'setTempWalletBindingResponse', - }, - ], - onError: [ - { - actions: ['setWalletBindingError', 'logWalletBindingFailure'], - target: 'showingWalletBindingError', - }, - ], - }, - }, - updatingPrivateKey: { - invoke: { - src: 'updatePrivateKey', - onDone: { - target: 'updatingContextVariables', - }, - onError: { - actions: [ - 'setWalletBindingError', - 'logWalletBindingFailure', - 'sendActivationFailedEndEvent', - ], - target: 'showingWalletBindingError', - }, - }, - }, - - updatingContextVariables: { - entry: [ - 'setWalletBindingId', - 'setThumbprintForWalletBindingId', - 'storeContext', - 'updatePrivateKey', - 'updateVc', - 'setWalletBindingErrorEmpty', - 'sendActivationSuccessEvent', - 'logWalletBindingSuccess', - send('SHOW_BINDING_STATUS'), - ], - on: { - SHOW_BINDING_STATUS: [ - { - cond: context => context.isMachineInKebabPopupState, - actions: 'sendWalletBindingSuccess', - target: '#vc-item.kebabPopUp', - }, - { - actions: 'sendWalletBindingSuccess', - target: 'idle', - }, - ], - }, - }, - }, - }, - { - actions: { - removeVcMetaDataFromStorage: send( - context => { - return StoreEvents.REMOVE_VC_METADATA( - MY_VCS_STORE_KEY, - context.vcMetadata.getVcKey(), - ); - }, - { - to: context => context.serviceRefs.store, - }, - ), - - removeVcMetaDataFromVcMachine: send( - context => { - return { - type: 'REMOVE_VC_FROM_CONTEXT', - vcMetadata: context.vcMetadata, - }; - }, - { - to: context => context.serviceRefs.vc, - }, - ), - - sendDownloadLimitExpire: send( - (_context, event) => { - return { - type: 'DOWNLOAD_LIMIT_EXPIRED', - vcMetadata: _context.vcMetadata, - }; - }, - { - to: context => context.serviceRefs.vc, - }, - ), - - sendVerificationError: send( - (context, event) => { - return { - type: 'VERIFY_VC_FAILED', - errorMessage: context.verificationErrorMessage, - vcMetadata: context.vcMetadata, - }; - }, - { - to: context => context.serviceRefs.vc, - }, - ), - - updateVerificationErrorMessage: assign({ - verificationErrorMessage: (context, event) => - (event.data as Error).message, - }), - - setWalletBindingError: assign({ - walletBindingError: () => { - const errorMessage = i18n.t(`errors.genericError`, { - ns: 'common', - }); - return errorMessage; - }, - bindingAuthFailedMessage: (_context, event) => { - const error = JSON.parse(JSON.stringify(event.data)).name; - if (error) { - return error; - } - return ''; - }, - }), - - setWalletBindingErrorEmpty: assign({ - walletBindingError: () => '', - bindingAuthFailedMessage: () => '', - }), - - sendWalletBindingSuccess: send( - context => { - return { - type: 'WALLET_BINDING_SUCCESS', - }; - }, - { - to: context => context.serviceRefs.vc, - }, - ), - - sendBackupEvent: send(BackupEvents.DATA_BACKUP(true), { - to: context => context.serviceRefs.backup, - }), - - sendActivationStartEvent: context => { - sendStartEvent( - getStartEventData( - context.isMachineInKebabPopupState - ? TelemetryConstants.FlowType.vcActivationFromKebab - : TelemetryConstants.FlowType.vcActivation, - ), - ); - sendInteractEvent( - getInteractEventData( - context.isMachineInKebabPopupState - ? TelemetryConstants.FlowType.vcActivationFromKebab - : TelemetryConstants.FlowType.vcActivation, - TelemetryConstants.InteractEventSubtype.click, - 'Activate Button', - ), - ); - }, - - sendActivationFailedEndEvent: (context, event, meta) => { - const [errorId, errorMessage] = - event.data?.message === 'Could not store private key in keystore' - ? [ - TelemetryConstants.ErrorId.updatePrivateKey, - TelemetryConstants.ErrorMessage.privateKeyUpdationFailed, - ] - : [ - TelemetryConstants.ErrorId.userCancel, - TelemetryConstants.ErrorMessage.activationCancelled, - ]; - sendEndEvent( - getEndEventData( - context.isMachineInKebabPopupState - ? TelemetryConstants.FlowType.vcActivationFromKebab - : TelemetryConstants.FlowType.vcActivation, - TelemetryConstants.EndEventStatus.failure, - { - errorId: errorId, - errorMessage: errorMessage, - }, - ), - ); - }, - - sendActivationSuccessEvent: context => - sendEndEvent( - getEndEventData( - context.isMachineInKebabPopupState - ? TelemetryConstants.FlowType.vcActivationFromKebab - : TelemetryConstants.FlowType.vcActivation, - TelemetryConstants.EndEventStatus.success, - ), - ), - - setPublicKey: assign({ - publicKey: (context, event) => { - if (!isHardwareKeystoreExists) { - return (event.data as KeyPair).public; - } - return event.data as string; - }, - }), - - setPrivateKey: assign({ - privateKey: (context, event) => (event.data as KeyPair).private, - }), - - updatePrivateKey: assign({ - privateKey: () => '', - }), - - setWalletBindingId: assign({ - walletBindingResponse: (context, event) => { - return isHardwareKeystoreExists - ? (event.data as WalletBindingResponse) - : context.tempWalletBindingIdResponse; - }, - }), - - setTempWalletBindingResponse: assign({ - tempWalletBindingIdResponse: (context, event) => - event.data as WalletBindingResponse, - }), - - setPinCard: assign({ - vcMetadata: context => - new VCMetadata({ - ...context.vcMetadata, - isPinned: !context.vcMetadata.isPinned, - }), - }), - - setVcMetadata: assign({ - vcMetadata: (_, event) => event.vcMetadata, - }), - - sendVcUpdated: send( - context => VcEvents.VC_METADATA_UPDATED(context.vcMetadata), - { - to: context => context.serviceRefs.vc, - }, - ), - - sendTamperedVc: send( - context => VcEvents.TAMPERED_VC(context.vcMetadata), - { - to: context => context.serviceRefs.vc, - }, - ), - - updateVc: send( - context => { - const {serviceRefs, ...vc} = context; - return {type: 'VC_DOWNLOADED', vc}; - }, - { - to: context => context.serviceRefs.vc, - }, - ), - - removeVcFromInProgressDownloads: send( - context => { - return { - type: 'REMOVE_VC_FROM_IN_PROGRESS_DOWNLOADS', - vcMetadata: context.vcMetadata, - }; - }, - { - to: context => context.serviceRefs.vc, - }, - ), - - addVcToInProgressDownloads: send( - context => { - return { - type: 'ADD_VC_TO_IN_PROGRESS_DOWNLOADS', - requestId: context.vcMetadata.requestId, - }; - }, - { - to: context => context.serviceRefs.vc, - }, - ), - - setThumbprintForWalletBindingId: send( - context => { - const {walletBindingResponse} = context; - const walletBindingIdKey = getBindingCertificateConstant( - walletBindingResponse.walletBindingId, - ); - return StoreEvents.SET( - walletBindingIdKey, - walletBindingResponse.thumbprint, - ); - }, - { - to: context => context.serviceRefs.store, - }, - ), - - refreshMyVcs: send( - () => ({ - type: 'REFRESH_MY_VCS', - }), - { - to: context => context.serviceRefs.vc, - }, - ), - - requestVcContext: send( - context => ({ - type: 'GET_VC_ITEM', - vcMetadata: context.vcMetadata, - }), - { - to: context => context.serviceRefs.vc, - }, - ), - - requestStoredContext: send( - context => StoreEvents.GET(context.vcMetadata.getVcKey()), - { - to: context => context.serviceRefs.store, - }, - ), - - storeContext: send( - context => { - const { - serviceRefs, - isMachineInKebabPopupState, - tempWalletBindingIdResponse, - ...data - } = context; - data.credentialRegistry = MIMOTO_BASE_URL; - return StoreEvents.SET(context.vcMetadata.getVcKey(), data); - }, - { - to: context => context.serviceRefs.store, - }, - ), - - incrementDownloadCounter: model.assign({ - downloadCounter: ({downloadCounter}) => downloadCounter + 1, - }), - - setMaxDownloadCount: model.assign({ - maxDownloadCount: (_context, event) => - Number((event.data as DownloadProps).maxDownloadLimit), - }), - - setDownloadInterval: model.assign({ - downloadInterval: (_context, event) => - Number((event.data as DownloadProps).downloadInterval), - }), - - setCredential: model.assign((context, event) => { - switch (event.type) { - case 'STORE_RESPONSE': - return { - ...context, - ...event.response, - vcMetadata: context.vcMetadata, - }; - case 'GET_VC_RESPONSE': - case 'CREDENTIAL_DOWNLOADED': - return { - ...context, - ...event.vc, - }; - } - }), - - logDownloaded: send( - context => { - const {serviceRefs, ...data} = context; - return ActivityLogEvents.LOG_ACTIVITY({ - _vcKey: context.vcMetadata.getVcKey(), - type: 'VC_DOWNLOADED', - id: context.vcMetadata.id, - idType: getIdType(context.vcMetadata.issuer), - timestamp: Date.now(), - deviceName: '', - vcLabel: data.id, - }); - }, - { - to: context => context.serviceRefs.activityLog, - }, - ), - sendTelemetryEvents: () => { - sendEndEvent({ - type: TelemetryConstants.FlowType.vcDownload, - status: TelemetryConstants.EndEventStatus.success, - }); - }, - - logWalletBindingSuccess: send( - context => - ActivityLogEvents.LOG_ACTIVITY({ - _vcKey: context.vcMetadata.getVcKey(), - type: 'WALLET_BINDING_SUCCESSFULL', - idType: getIdType(context.vcMetadata.issuer), - id: context.vcMetadata.id, - timestamp: Date.now(), - deviceName: '', - vcLabel: context.vcMetadata.id, - }), - { - to: context => context.serviceRefs.activityLog, - }, - ), - - logWalletBindingFailure: send( - context => - ActivityLogEvents.LOG_ACTIVITY({ - _vcKey: context.vcMetadata.getVcKey(), - type: 'WALLET_BINDING_FAILURE', - timestamp: Date.now(), - deviceName: '', - id: context.vcMetadata.id, - idType: getIdType(context.vcMetadata.issuer), - vcLabel: context.vcMetadata.id, - }), - { - to: context => context.serviceRefs.activityLog, - }, - ), - - setTransactionId: assign({ - transactionId: () => String(new Date().valueOf()).substring(3, 13), - }), - - clearTransactionId: assign({transactionId: ''}), - - setOtp: model.assign({ - otp: (_, event) => event.otp, - }), - - setVcKey: model.assign({ - vcMetadata: (_, event) => event.vcMetadata, - }), - - setOtpError: assign({ - otpError: (_context, event) => - (event as ErrorPlatformEvent).data.message, - }), - - setPhoneNumber: assign({ - phoneNumber: (_context, event) => event.data.response.maskedMobile, - }), - - setEmail: model.assign({ - email: (_context, event) => event.data.response.maskedEmail, - }), - - clearOtp: assign({otp: ''}), - - removeVcItem: send( - _context => { - return StoreEvents.REMOVE( - MY_VCS_STORE_KEY, - _context.vcMetadata.getVcKey(), - ); - }, - {to: context => context.serviceRefs.store}, - ), - - closeViewVcModal: send('DISMISS_MODAL', { - to: () => getHomeMachineService(), - }), - - logVCremoved: send( - (context, _) => - ActivityLogEvents.LOG_ACTIVITY({ - _vcKey: context.vcMetadata.getVcKey(), - type: 'VC_REMOVED', - timestamp: Date.now(), - deviceName: '', - vcLabel: context.vcMetadata.id, - id: context.vcMetadata.id, - idType: getIdType(context.vcMetadata.issuer), - }), - { - to: context => context.serviceRefs.activityLog, - }, - ), - }, - - services: { - isUserSignedAlready: () => async () => { - return await Cloud.isSignedInAlready(); - }, - - loadDownloadLimitConfig: async context => { - var resp = await getAllConfigurations(); - const maxLimit: number = resp.vcDownloadMaxRetry; - const vcDownloadPoolInterval: number = resp.vcDownloadPoolInterval; - - const downloadProps: DownloadProps = { - maxDownloadLimit: maxLimit, - downloadInterval: vcDownloadPoolInterval, - }; - return downloadProps; - }, - - checkDownloadExpiryLimit: async context => { - if (context.downloadCounter > context.maxDownloadCount) { - throw new Error( - 'Download limit expired for request id: ' + - context.vcMetadata.requestId, - ); - } - }, - - addWalletBindnigId: async context => { - const response = await request( - API_URLS.walletBinding.method, - API_URLS.walletBinding.buildURL(), - { - requestTime: String(new Date().toISOString()), - request: { - authFactorType: 'WLA', - format: 'jwt', - individualId: context.vcMetadata.id, - transactionId: context.transactionId, - publicKey: context.publicKey, - challengeList: [ - { - authFactorType: 'OTP', - challenge: context.otp, - format: 'alpha-numeric', - }, - ], - }, - }, - ); - const certificate = response.response.certificate; - await savePrivateKey( - getBindingCertificateConstant(context.vcMetadata.id), - certificate, - ); - - const walletResponse: WalletBindingResponse = { - walletBindingId: response.response.encryptedWalletBindingId, - keyId: response.response.keyId, - thumbprint: response.response.thumbprint, - expireDateTime: response.response.expireDateTime, - }; - return walletResponse; - }, - - updatePrivateKey: async context => { - const hasSetPrivateKey: boolean = await savePrivateKey( - context.tempWalletBindingIdResponse.walletBindingId, - context.privateKey, - ); - if (!hasSetPrivateKey) { - throw new Error('Could not store private key in keystore.'); - } - return ''; - }, - - generateKeyPair: async context => { - if (!isHardwareKeystoreExists) { - return await generateKeys(); - } - const isBiometricsEnabled = SecureKeystore.hasBiometricsEnabled(); - return SecureKeystore.generateKeyPair( - context.vcMetadata.id, - isBiometricsEnabled, - 0, - ); - }, - - requestBindingOtp: async context => { - const response = await request( - API_URLS.bindingOtp.method, - API_URLS.bindingOtp.buildURL(), - { - requestTime: String(new Date().toISOString()), - request: { - individualId: context.vcMetadata.id, - otpChannels: ['EMAIL', 'PHONE'], - }, - }, - ); - if (response.response == null) { - throw new Error('Could not process request'); - } - return response; - }, - - checkStatus: context => (callback, onReceive) => { - const pollInterval = setInterval( - () => callback(model.events.POLL()), - context.downloadInterval, - ); - - onReceive(async event => { - if (event.type === 'POLL_STATUS') { - try { - const response = await request( - API_URLS.credentialStatus.method, - API_URLS.credentialStatus.buildURL( - context.vcMetadata.requestId, - ), - ); - switch (response.response?.statusCode) { - case 'NEW': - break; - case 'ISSUED': - case 'printing': - callback(model.events.DOWNLOAD_READY()); - break; - case 'FAILED': - default: - callback(model.events.FAILED()); - clearInterval(pollInterval); - break; - } - } catch (error) { - callback(model.events.FAILED()); - clearInterval(pollInterval); - } - } - }); - - return () => clearInterval(pollInterval); - }, - - downloadCredential: context => (callback, onReceive) => { - const pollInterval = setInterval( - () => callback(model.events.POLL()), - context.downloadInterval, - ); - - onReceive(async event => { - if (event.type === 'POLL_DOWNLOAD') { - const response: CredentialDownloadResponse = await request( - API_URLS.credentialDownload.method, - API_URLS.credentialDownload.buildURL(), - { - individualId: context.vcMetadata.id, - requestId: context.vcMetadata.requestId, - }, - ); - - callback( - model.events.CREDENTIAL_DOWNLOADED({ - credential: response.credential, - verifiableCredential: response.verifiableCredential, - generatedOn: new Date(), - id: context.vcMetadata.id, - idType: context.vcMetadata.idType, - requestId: context.vcMetadata.requestId, - lastVerifiedOn: null, - locked: context.locked, - walletBindingResponse: null, - credentialRegistry: '', - }), - ); - } - }); - - return () => clearInterval(pollInterval); - }, - - verifyCredential: async context => { - if (context.verifiableCredential) { - const verificationResult = await verifyCredential( - context.verifiableCredential, - ); - if (!verificationResult.isVerified) { - throw new Error(verificationResult.errorMessage); - } - } - }, - - requestOtp: async context => { - try { - return request( - API_URLS.requestOtp.method, - API_URLS.requestOtp.buildURL(), - { - individualId: context.vcMetadata.id, - individualIdType: context.vcMetadata.idType, - otpChannel: ['EMAIL', 'PHONE'], - transactionID: context.transactionId, - }, - ); - } catch (error) { - console.error(error); - } - }, - }, - - guards: { - isSignedIn: (_context, event) => - (event.data as isSignedInResult).isSignedIn, - hasCredential: (_, event) => { - const vc = - event.type === 'GET_VC_RESPONSE' ? event.vc : event.response; - - return vc?.credential != null && vc?.verifiableCredential != null; - }, - - isDownloadAllowed: _context => { - return _context.downloadCounter <= _context.maxDownloadCount; - }, - - isCustomSecureKeystore: () => isHardwareKeystoreExists, - }, - }, - ); - -export const createExistingMosipVCItemMachine = ( - serviceRefs: AppServices, - vcMetadata: VCMetadata, -) => { - return ExistingMosipVCItemMachine.withContext({ - ...ExistingMosipVCItemMachine.context, - serviceRefs, - vcMetadata, - }); -}; - -type State = StateFrom; - -export function selectVc(state: State) { - const {serviceRefs, ...data} = state.context; - return data; -} - -export function selectId(state: State) { - return state.context.vcMetadata.id; -} - -export function selectRequestBindingOtp(state: State) { - return state.matches('requestingBindingOtp'); -} - -export function selectIsSavingFailedInIdle(state: State) { - return state.matches('checkingServerData.savingFailed.idle'); -} diff --git a/machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine.typegen.ts b/machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine.typegen.ts deleted file mode 100644 index 6ee2f08f..00000000 --- a/machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine.typegen.ts +++ /dev/null @@ -1,301 +0,0 @@ -// This file was automatically generated. Edits will be overwritten - -export interface Typegen0 { - '@@xstate/typegen': true; - internalEvents: { - '': {type: ''}; - 'done.invoke.checkStatus': { - type: 'done.invoke.checkStatus'; - data: unknown; - __tip: 'See the XState TS docs to learn how to strongly type this.'; - }; - 'done.invoke.downloadCredential': { - type: 'done.invoke.downloadCredential'; - data: unknown; - __tip: 'See the XState TS docs to learn how to strongly type this.'; - }; - 'done.invoke.vc-item.addKeyPair:invocation[0]': { - type: 'done.invoke.vc-item.addKeyPair:invocation[0]'; - data: unknown; - __tip: 'See the XState TS docs to learn how to strongly type this.'; - }; - 'done.invoke.vc-item.addingWalletBindingId:invocation[0]': { - type: 'done.invoke.vc-item.addingWalletBindingId:invocation[0]'; - data: unknown; - __tip: 'See the XState TS docs to learn how to strongly type this.'; - }; - 'done.invoke.vc-item.checkingServerData.loadDownloadLimitConfig:invocation[0]': { - type: 'done.invoke.vc-item.checkingServerData.loadDownloadLimitConfig:invocation[0]'; - data: unknown; - __tip: 'See the XState TS docs to learn how to strongly type this.'; - }; - 'done.invoke.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]': { - type: 'done.invoke.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]'; - data: unknown; - __tip: 'See the XState TS docs to learn how to strongly type this.'; - }; - 'done.invoke.vc-item.kebabPopUp.removingVc.triggerAutoBackup:invocation[0]': { - type: 'done.invoke.vc-item.kebabPopUp.removingVc.triggerAutoBackup:invocation[0]'; - data: unknown; - __tip: 'See the XState TS docs to learn how to strongly type this.'; - }; - 'done.invoke.vc-item.requestingBindingOtp:invocation[0]': { - type: 'done.invoke.vc-item.requestingBindingOtp:invocation[0]'; - data: unknown; - __tip: 'See the XState TS docs to learn how to strongly type this.'; - }; - 'done.invoke.vc-item.updatingPrivateKey:invocation[0]': { - type: 'done.invoke.vc-item.updatingPrivateKey:invocation[0]'; - data: unknown; - __tip: 'See the XState TS docs to learn how to strongly type this.'; - }; - 'done.invoke.vc-item.verifyingCredential.triggerAutoBackupForVcDownload:invocation[0]': { - type: 'done.invoke.vc-item.verifyingCredential.triggerAutoBackupForVcDownload:invocation[0]'; - data: unknown; - __tip: 'See the XState TS docs to learn how to strongly type this.'; - }; - 'done.invoke.vc-item.verifyingCredential:invocation[0]': { - type: 'done.invoke.vc-item.verifyingCredential:invocation[0]'; - data: unknown; - __tip: 'See the XState TS docs to learn how to strongly type this.'; - }; - 'error.platform.checkStatus': { - type: 'error.platform.checkStatus'; - data: unknown; - }; - 'error.platform.downloadCredential': { - type: 'error.platform.downloadCredential'; - data: unknown; - }; - 'error.platform.vc-item.acceptingBindingOtp.resendOTP:invocation[0]': { - type: 'error.platform.vc-item.acceptingBindingOtp.resendOTP:invocation[0]'; - data: unknown; - }; - 'error.platform.vc-item.addKeyPair:invocation[0]': { - type: 'error.platform.vc-item.addKeyPair:invocation[0]'; - data: unknown; - }; - 'error.platform.vc-item.addingWalletBindingId:invocation[0]': { - type: 'error.platform.vc-item.addingWalletBindingId:invocation[0]'; - data: unknown; - }; - 'error.platform.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]': { - type: 'error.platform.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]'; - data: unknown; - }; - 'error.platform.vc-item.requestingBindingOtp:invocation[0]': { - type: 'error.platform.vc-item.requestingBindingOtp:invocation[0]'; - data: unknown; - }; - 'error.platform.vc-item.updatingPrivateKey:invocation[0]': { - type: 'error.platform.vc-item.updatingPrivateKey:invocation[0]'; - data: unknown; - }; - 'error.platform.vc-item.verifyingCredential:invocation[0]': { - type: 'error.platform.vc-item.verifyingCredential:invocation[0]'; - data: unknown; - }; - 'xstate.init': {type: 'xstate.init'}; - }; - invokeSrcNameMap: { - addWalletBindnigId: 'done.invoke.vc-item.addingWalletBindingId:invocation[0]'; - checkDownloadExpiryLimit: 'done.invoke.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]'; - checkStatus: 'done.invoke.checkStatus'; - downloadCredential: 'done.invoke.downloadCredential'; - generateKeyPair: 'done.invoke.vc-item.addKeyPair:invocation[0]'; - isUserSignedAlready: - | 'done.invoke.vc-item.kebabPopUp.removingVc.triggerAutoBackup:invocation[0]' - | 'done.invoke.vc-item.verifyingCredential.triggerAutoBackupForVcDownload:invocation[0]'; - loadDownloadLimitConfig: 'done.invoke.vc-item.checkingServerData.loadDownloadLimitConfig:invocation[0]'; - requestBindingOtp: - | 'done.invoke.vc-item.acceptingBindingOtp.resendOTP:invocation[0]' - | 'done.invoke.vc-item.requestingBindingOtp:invocation[0]'; - updatePrivateKey: 'done.invoke.vc-item.updatingPrivateKey:invocation[0]'; - verifyCredential: 'done.invoke.vc-item.verifyingCredential:invocation[0]'; - }; - missingImplementations: { - actions: never; - delays: never; - guards: never; - services: never; - }; - eventsCausingActions: { - addVcToInProgressDownloads: 'STORE_RESPONSE'; - clearOtp: - | '' - | 'CANCEL' - | 'DISMISS' - | 'SHOW_BINDING_STATUS' - | 'done.invoke.vc-item.requestingBindingOtp:invocation[0]' - | 'done.invoke.vc-item.verifyingCredential.triggerAutoBackupForVcDownload:invocation[0]'; - clearTransactionId: - | '' - | 'CANCEL' - | 'DISMISS' - | 'SHOW_BINDING_STATUS' - | 'done.invoke.vc-item.verifyingCredential.triggerAutoBackupForVcDownload:invocation[0]'; - closeViewVcModal: 'CLOSE_VC_MODAL' | 'STORE_RESPONSE'; - incrementDownloadCounter: - | 'POLL' - | 'done.invoke.vc-item.checkingServerData.loadDownloadLimitConfig:invocation[0]'; - logDownloaded: 'STORE_RESPONSE'; - logVCremoved: - | 'STORE_RESPONSE' - | 'done.invoke.vc-item.kebabPopUp.removingVc.triggerAutoBackup:invocation[0]'; - logWalletBindingFailure: - | 'error.platform.vc-item.addKeyPair:invocation[0]' - | 'error.platform.vc-item.addingWalletBindingId:invocation[0]' - | 'error.platform.vc-item.requestingBindingOtp:invocation[0]' - | 'error.platform.vc-item.updatingPrivateKey:invocation[0]'; - logWalletBindingSuccess: - | 'done.invoke.vc-item.addingWalletBindingId:invocation[0]' - | 'done.invoke.vc-item.updatingPrivateKey:invocation[0]'; - refreshMyVcs: - | 'STORE_RESPONSE' - | 'done.invoke.vc-item.kebabPopUp.removingVc.triggerAutoBackup:invocation[0]'; - removeVcFromInProgressDownloads: 'STORE_RESPONSE'; - removeVcItem: 'CONFIRM'; - removeVcMetaDataFromStorage: - | 'STORE_ERROR' - | 'error.platform.vc-item.verifyingCredential:invocation[0]'; - removeVcMetaDataFromVcMachine: 'DISMISS'; - requestStoredContext: 'GET_VC_RESPONSE' | 'REFRESH'; - requestVcContext: 'DISMISS' | 'xstate.init'; - sendActivationFailedEndEvent: - | 'DISMISS' - | 'error.platform.vc-item.updatingPrivateKey:invocation[0]'; - sendActivationStartEvent: 'CONFIRM'; - sendActivationSuccessEvent: - | 'done.invoke.vc-item.addingWalletBindingId:invocation[0]' - | 'done.invoke.vc-item.updatingPrivateKey:invocation[0]'; - sendBackupEvent: - | 'done.invoke.vc-item.kebabPopUp.removingVc.triggerAutoBackup:invocation[0]' - | 'done.invoke.vc-item.verifyingCredential.triggerAutoBackupForVcDownload:invocation[0]'; - sendDownloadLimitExpire: - | 'FAILED' - | 'error.platform.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]'; - sendTamperedVc: 'TAMPERED_VC'; - sendTelemetryEvents: 'STORE_RESPONSE'; - sendVcUpdated: 'PIN_CARD'; - sendVerificationError: 'STORE_RESPONSE'; - sendWalletBindingSuccess: 'SHOW_BINDING_STATUS'; - setCredential: - | 'CREDENTIAL_DOWNLOADED' - | 'GET_VC_RESPONSE' - | 'STORE_RESPONSE'; - setDownloadInterval: 'done.invoke.vc-item.checkingServerData.loadDownloadLimitConfig:invocation[0]'; - setMaxDownloadCount: 'done.invoke.vc-item.checkingServerData.loadDownloadLimitConfig:invocation[0]'; - setOtp: 'INPUT_OTP'; - setPinCard: 'PIN_CARD'; - setPrivateKey: 'done.invoke.vc-item.addKeyPair:invocation[0]'; - setPublicKey: 'done.invoke.vc-item.addKeyPair:invocation[0]'; - setTempWalletBindingResponse: 'done.invoke.vc-item.addingWalletBindingId:invocation[0]'; - setThumbprintForWalletBindingId: - | 'done.invoke.vc-item.addingWalletBindingId:invocation[0]' - | 'done.invoke.vc-item.updatingPrivateKey:invocation[0]'; - setVcKey: 'REMOVE'; - setVcMetadata: 'UPDATE_VC_METADATA'; - setWalletBindingError: - | 'error.platform.vc-item.acceptingBindingOtp.resendOTP:invocation[0]' - | 'error.platform.vc-item.addKeyPair:invocation[0]' - | 'error.platform.vc-item.addingWalletBindingId:invocation[0]' - | 'error.platform.vc-item.requestingBindingOtp:invocation[0]' - | 'error.platform.vc-item.updatingPrivateKey:invocation[0]'; - setWalletBindingErrorEmpty: - | 'CANCEL' - | 'done.invoke.vc-item.addingWalletBindingId:invocation[0]' - | 'done.invoke.vc-item.updatingPrivateKey:invocation[0]'; - setWalletBindingId: - | 'done.invoke.vc-item.addingWalletBindingId:invocation[0]' - | 'done.invoke.vc-item.updatingPrivateKey:invocation[0]'; - storeContext: - | 'done.invoke.vc-item.addingWalletBindingId:invocation[0]' - | 'done.invoke.vc-item.updatingPrivateKey:invocation[0]' - | 'done.invoke.vc-item.verifyingCredential:invocation[0]'; - updatePrivateKey: - | 'done.invoke.vc-item.addingWalletBindingId:invocation[0]' - | 'done.invoke.vc-item.updatingPrivateKey:invocation[0]'; - updateVc: - | 'STORE_RESPONSE' - | 'done.invoke.vc-item.addingWalletBindingId:invocation[0]' - | 'done.invoke.vc-item.updatingPrivateKey:invocation[0]'; - updateVerificationErrorMessage: 'error.platform.vc-item.verifyingCredential:invocation[0]'; - }; - eventsCausingDelays: {}; - eventsCausingGuards: { - hasCredential: 'GET_VC_RESPONSE' | 'STORE_RESPONSE'; - isCustomSecureKeystore: - | 'done.invoke.vc-item.addKeyPair:invocation[0]' - | 'done.invoke.vc-item.addingWalletBindingId:invocation[0]'; - isDownloadAllowed: 'POLL'; - isSignedIn: - | 'done.invoke.vc-item.kebabPopUp.removingVc.triggerAutoBackup:invocation[0]' - | 'done.invoke.vc-item.verifyingCredential.triggerAutoBackupForVcDownload:invocation[0]'; - }; - eventsCausingServices: { - addWalletBindnigId: 'done.invoke.vc-item.addKeyPair:invocation[0]'; - checkDownloadExpiryLimit: - | 'POLL' - | 'done.invoke.vc-item.checkingServerData.loadDownloadLimitConfig:invocation[0]'; - checkStatus: 'done.invoke.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]'; - downloadCredential: 'DOWNLOAD_READY'; - generateKeyPair: 'INPUT_OTP'; - isUserSignedAlready: 'STORE_RESPONSE'; - loadDownloadLimitConfig: 'STORE_ERROR' | 'STORE_RESPONSE'; - requestBindingOtp: 'CONFIRM' | 'RESEND_OTP'; - updatePrivateKey: 'done.invoke.vc-item.addingWalletBindingId:invocation[0]'; - verifyCredential: '' | 'VERIFY'; - }; - matchesStates: - | 'acceptingBindingOtp' - | 'acceptingBindingOtp.idle' - | 'acceptingBindingOtp.resendOTP' - | 'addKeyPair' - | 'addingWalletBindingId' - | 'checkingServerData' - | 'checkingServerData.checkingStatus' - | 'checkingServerData.downloadingCredential' - | 'checkingServerData.loadDownloadLimitConfig' - | 'checkingServerData.savingFailed' - | 'checkingServerData.savingFailed.idle' - | 'checkingServerData.savingFailed.viewingVc' - | 'checkingServerData.verifyingDownloadLimitExpiry' - | 'checkingStore' - | 'checkingVc' - | 'checkingVerificationStatus' - | 'handleVCVerificationFailure' - | 'idle' - | 'kebabPopUp' - | 'kebabPopUp.idle' - | 'kebabPopUp.removeWallet' - | 'kebabPopUp.removingVc' - | 'kebabPopUp.removingVc.triggerAutoBackup' - | 'kebabPopUp.showActivities' - | 'pinCard' - | 'requestingBindingOtp' - | 'showBindingWarning' - | 'showingWalletBindingError' - | 'updatingContextVariables' - | 'updatingPrivateKey' - | 'verifyingCredential' - | 'verifyingCredential.idle' - | 'verifyingCredential.triggerAutoBackupForVcDownload' - | { - acceptingBindingOtp?: 'idle' | 'resendOTP'; - checkingServerData?: - | 'checkingStatus' - | 'downloadingCredential' - | 'loadDownloadLimitConfig' - | 'savingFailed' - | 'verifyingDownloadLimitExpiry' - | {savingFailed?: 'idle' | 'viewingVc'}; - kebabPopUp?: - | 'idle' - | 'removeWallet' - | 'removingVc' - | 'showActivities' - | {removingVc?: 'triggerAutoBackup'}; - verifyingCredential?: 'idle' | 'triggerAutoBackupForVcDownload'; - }; - tags: never; -} diff --git a/machines/VCItemMachine/commonSelectors.ts b/machines/VCItemMachine/commonSelectors.ts deleted file mode 100644 index de2d5937..00000000 --- a/machines/VCItemMachine/commonSelectors.ts +++ /dev/null @@ -1,91 +0,0 @@ -import {StateFrom} from 'xstate'; -import {EsignetMosipVCItemMachine} from './EsignetMosipVCItem/EsignetMosipVCItemMachine'; -import {ExistingMosipVCItemMachine} from './ExistingMosipVCItem/ExistingMosipVCItemMachine'; - -type State = StateFrom< - typeof ExistingMosipVCItemMachine & typeof EsignetMosipVCItemMachine ->; - -export function selectVerifiableCredential(state: State) { - return state.context.verifiableCredential; -} - -export function selectKebabPopUp(state: State) { - return state.context.isMachineInKebabPopupState; -} - -export function selectContext(state: State) { - return state.context; -} - -export function selectGeneratedOn(state: State) { - return state.context.generatedOn; -} - -export function selectWalletBindingSuccess(state: State) { - return state.context.walletBindingSuccess; -} - -export function selectIsPhoneNumber(state: State) { - return state.context.phoneNumber; -} - -export function selectIsEmail(state: State) { - return state.context.email; -} - -export function selectEmptyWalletBindingId(state: State) { - var val = state.context.walletBindingResponse - ? state.context.walletBindingResponse.walletBindingId - : undefined; - return val == undefined || val == null || val.length <= 0 ? true : false; -} - -export function selectWalletBindingError(state: State) { - return state.context.walletBindingError; -} - -export function selectBindingAuthFailedError(state: State) { - return state.context.bindingAuthFailedMessage; -} - -export function selectAcceptingBindingOtp(state: State) { - return state.matches('acceptingBindingOtp'); -} - -export function selectKebabPopUpShowWalletBindingError(state: State) { - return state.matches('showingWalletBindingError'); -} - -export function selectWalletBindingInProgress(state: State) { - return state.matches('requestingBindingOtp') || - state.matches('addingWalletBindingId') || - state.matches('addKeyPair') || - state.matches('updatingPrivateKey') - ? true - : false; -} - -export function selectBindingWarning(state: State) { - return state.matches('showBindingWarning'); -} - -export function selectRemoveWalletWarning(state: State) { - return state.matches('kebabPopUp.removeWallet'); -} - -export function selectIsPinned(state: State) { - return state.context.vcMetadata.isPinned; -} - -export function selectOtpError(state: State) { - return state.context.otpError; -} - -export function selectShowActivities(state: State) { - return state.matches('kebabPopUp.showActivities'); -} - -export function selectShowWalletBindingError(state: State) { - return state.matches('showingWalletBindingError'); -} diff --git a/machines/VerifiableCredential/VCItemMachine/VCItemActions.ts b/machines/VerifiableCredential/VCItemMachine/VCItemActions.ts new file mode 100644 index 00000000..f6969f1f --- /dev/null +++ b/machines/VerifiableCredential/VCItemMachine/VCItemActions.ts @@ -0,0 +1,431 @@ +import {assign, send} from 'xstate'; +import {CommunicationDetails} from '../../../shared/Utils'; +import {StoreEvents} from '../../store'; +import {VCMetadata} from '../../../shared/VCMetadata'; +import {MIMOTO_BASE_URL, MY_VCS_STORE_KEY} from '../../../shared/constants'; +import {KeyPair} from 'react-native-rsa-native'; +import i18n from '../../../i18n'; +import {getHomeMachineService} from '../../../screens/Home/HomeScreenController'; +import {DownloadProps} from '../../../shared/api'; +import {isHardwareKeystoreExists} from '../../../shared/cryptoutil/cryptoUtil'; +import {getBindingCertificateConstant} from '../../../shared/keystore/SecureKeystore'; +import {getIdType} from '../../../shared/openId4VCI/Utils'; +import {TelemetryConstants} from '../../../shared/telemetry/TelemetryConstants'; +import { + sendStartEvent, + getStartEventData, + sendInteractEvent, + getInteractEventData, + sendEndEvent, + getEndEventData, + sendErrorEvent, + getErrorEventData, +} from '../../../shared/telemetry/TelemetryUtils'; +import {WalletBindingResponse} from '../../../types/VC/vc'; +import {ActivityLogEvents} from '../../activityLog'; +import {BackupEvents} from '../../backupAndRestore/backup'; +import {VcEvents} from '../VCMetaMachine/vc'; + +export const VCItemActions = model => { + return { + setCommunicationDetails: model.assign({ + communicationDetails: (_context, event) => { + const communicationDetails: CommunicationDetails = { + phoneNumber: event.data.response.maskedMobile, + emailId: event.data.response.maskedEmail, + }; + return communicationDetails; + }, + }), + requestVcContext: send( + (context: any) => ({ + type: 'GET_VC_ITEM', + vcMetadata: context.vcMetadata, + }), + { + to: context => context.serviceRefs.vc, + }, + ), + requestStoredContext: send( + (context: any) => { + return StoreEvents.GET( + VCMetadata.fromVC(context.vcMetadata).getVcKey(), + ); + }, + { + to: context => context.serviceRefs.store, + }, + ), + setContext: model.assign((context, event) => { + const {...data} = event.response; + return {...context, ...data}; + }), + setCredential: model.assign((context, event) => { + return { + ...context, + ...event.response, + vcMetadata: context.vcMetadata, + }; + }), + storeContext: send( + (context: any) => { + const {serviceRefs, isMachineInKebabPopupState, ...data} = context; + data.credentialRegistry = MIMOTO_BASE_URL; + return StoreEvents.SET( + VCMetadata.fromVC(context.vcMetadata).getVcKey(), + data, + ); + }, + { + to: context => context.serviceRefs.store, + }, + ), + setVcMetadata: assign({ + vcMetadata: (_, event) => event.vcMetadata, + }), + storeVcInContext: send( + //todo : separate handling done for openid4vci , handle commonly from vc machine + (context: any) => { + const {serviceRefs, ...verifiableCredential} = context; + return { + type: 'VC_DOWNLOADED', + vc: verifiableCredential, + vcMetadata: context.vcMetadata, + }; + }, + { + to: context => context.serviceRefs.vc, + }, + ), + removeVcMetaDataFromStorage: send( + (context: any) => { + return StoreEvents.REMOVE_VC_METADATA( + MY_VCS_STORE_KEY, + context.vcMetadata.getVcKey(), + ); + }, + { + to: context => context.serviceRefs.store, + }, + ), + removeVcMetaDataFromVcMachineContext: send( + (context: any) => { + return { + type: 'REMOVE_VC_FROM_CONTEXT', + vcMetadata: context.vcMetadata, + }; + }, + { + to: context => context.serviceRefs.vc, + }, + ), + sendDownloadLimitExpire: send( + (context: any, event) => { + return { + type: 'DOWNLOAD_LIMIT_EXPIRED', + vcMetadata: context.vcMetadata, + }; + }, + { + to: context => context.serviceRefs.vc, + }, + ), + + sendVerificationError: send( + (context: any, _event) => { + return { + type: 'VERIFY_VC_FAILED', + errorMessage: context.error, + vcMetadata: context.vcMetadata, + }; + }, + { + to: context => context.serviceRefs.vc, + }, + ), + refreshAllVcs: send( + () => ({ + type: 'REFRESH_MY_VCS', + }), + { + to: (context: any) => context.serviceRefs.vc, + }, + ), + + setPinCard: assign({ + vcMetadata: (context: any) => + new VCMetadata({ + ...context.vcMetadata, + isPinned: !context.vcMetadata.isPinned, + }), + }), + sendBackupEvent: send(BackupEvents.DATA_BACKUP(true), { + to: (context: any) => context.serviceRefs.backup, + }), + //todo: revisit on this for naming and impl + sendVcUpdated: send( + (context: any) => VcEvents.VC_METADATA_UPDATED(context.vcMetadata), + { + to: (context: any) => context.serviceRefs.vc, + }, + ), + sendTamperedVc: send( + (context: any) => VcEvents.TAMPERED_VC(context.vcMetadata), + { + to: context => context.serviceRefs.vc, + }, + ), + setErrorAsWalletBindingError: assign({ + error: () => + i18n.t('errors.genericError', { + ns: 'common', + }), + }), + setErrorAsVerificationError: assign({ + //todo handle error message from different actions + error: (_context, event) => (event.data as Error).message, + }), + unSetError: assign({ + error: () => '', + }), + unSetBindingTransactionId: assign({bindingTransactionId: () => ''}), + sendWalletBindingSuccess: send( + context => { + return { + type: 'WALLET_BINDING_SUCCESS', + }; + }, + { + to: (context: any) => context.serviceRefs.vc, + }, + ), + setWalletBindingResponse: assign({ + walletBindingResponse: (_context, event) => + event.data as WalletBindingResponse, + }), + incrementDownloadCounter: model.assign({ + downloadCounter: ({downloadCounter}) => downloadCounter + 1, + }), + + setMaxDownloadCount: model.assign({ + maxDownloadCount: (_context, event) => + Number((event.data as DownloadProps).maxDownloadLimit), + }), + + setDownloadInterval: model.assign({ + downloadInterval: (_context, event) => + Number((event.data as DownloadProps).downloadInterval), + }), + sendActivationStartEvent: context => { + sendStartEvent( + getStartEventData( + context.isMachineInKebabPopupState + ? TelemetryConstants.FlowType.vcActivationFromKebab + : TelemetryConstants.FlowType.vcActivation, + ), + ); + sendInteractEvent( + getInteractEventData( + context.isMachineInKebabPopupState + ? TelemetryConstants.FlowType.vcActivationFromKebab + : TelemetryConstants.FlowType.vcActivation, + TelemetryConstants.InteractEventSubtype.click, + 'Activate Button', + ), + ); + }, + + sendUserCancelledActivationFailedEndEvent: context => { + const [errorId, errorMessage] = [ + TelemetryConstants.ErrorId.userCancel, + TelemetryConstants.ErrorMessage.activationCancelled, + ]; + sendEndEvent( + getEndEventData( + context.isMachineInKebabPopupState + ? TelemetryConstants.FlowType.vcActivationFromKebab + : TelemetryConstants.FlowType.vcActivation, + TelemetryConstants.EndEventStatus.failure, + { + errorId: errorId, + errorMessage: errorMessage, + }, + ), + ); + }, + + sendWalletBindingErrorEvent: (context, event) => { + if (context.error) { + const error = JSON.parse(JSON.stringify(event.data)).name + ? JSON.parse(JSON.stringify(event.data)).name + '-' + context.error + : context.error; + sendErrorEvent( + getErrorEventData( + TelemetryConstants.FlowType.vcActivation, + TelemetryConstants.ErrorId.activationFailed, + error, + ), + ); + } + sendEndEvent( + getEndEventData( + TelemetryConstants.FlowType.vcActivation, + TelemetryConstants.EndEventStatus.failure, + ), + ); + }, + + sendActivationSuccessEvent: context => + sendEndEvent( + getEndEventData( + context.isMachineInKebabPopupState + ? TelemetryConstants.FlowType.vcActivationFromKebab + : TelemetryConstants.FlowType.vcActivation, + TelemetryConstants.EndEventStatus.success, + ), + ), + + setPublicKey: assign({ + publicKey: (_context, event) => { + if (!isHardwareKeystoreExists) { + return (event.data as KeyPair).public; + } + return event.data as string; + }, + }), + + setPrivateKey: assign({ + privateKey: (_context, event) => (event.data as KeyPair).private, + }), + resetPrivateKey: assign({ + privateKey: () => '', + }), + setThumbprintForWalletBindingId: send( + (context: any) => { + const {walletBindingResponse} = context; + const walletBindingIdKey = getBindingCertificateConstant( + walletBindingResponse.walletBindingId, + ); + return StoreEvents.SET( + walletBindingIdKey, + walletBindingResponse.thumbprint, + ); + }, + { + to: context => context.serviceRefs.store, + }, + ), + setOTP: model.assign({ + OTP: (_, event) => event.OTP, + }), + + unSetOTP: model.assign({OTP: () => ''}), + removeVcItem: send( + (context: any) => { + return StoreEvents.REMOVE( + MY_VCS_STORE_KEY, + context.vcMetadata.getVcKey(), + ); + }, + {to: context => context.serviceRefs.store}, + ), + setVcKey: model.assign({ + vcMetadata: (_, event) => event.vcMetadata, + }), + removeVcFromInProgressDownloads: send( + (context: any) => { + return { + type: 'REMOVE_VC_FROM_IN_PROGRESS_DOWNLOADS', + vcMetadata: context.vcMetadata, + }; + }, + { + to: context => context.serviceRefs.vc, + }, + ), + + addVcToInProgressDownloads: send( + (context: any) => { + return { + type: 'ADD_VC_TO_IN_PROGRESS_DOWNLOADS', + requestId: context.vcMetadata.requestId, + }; + }, + { + to: context => context.serviceRefs.vc, + }, + ), + sendTelemetryEvents: () => { + sendEndEvent({ + type: TelemetryConstants.FlowType.vcDownload, + status: TelemetryConstants.EndEventStatus.success, + }); + }, + closeViewVcModal: send('DISMISS_MODAL', { + to: () => getHomeMachineService(), + }), + logDownloaded: send( + (context: any) => { + const {serviceRefs, ...data} = context; + return ActivityLogEvents.LOG_ACTIVITY({ + _vcKey: context.vcMetadata.getVcKey(), + type: 'VC_DOWNLOADED', + id: context.vcMetadata.id, + idType: getIdType(context.vcMetadata.issuer), + timestamp: Date.now(), + deviceName: '', + vcLabel: data.id, + }); + }, + { + to: context => context.serviceRefs.activityLog, + }, + ), + logRemovedVc: send( + (context: any, _) => + ActivityLogEvents.LOG_ACTIVITY({ + idType: getIdType(context.vcMetadata.issuer), + id: context.vcMetadata.id, + _vcKey: VCMetadata.fromVC(context.vcMetadata).getVcKey(), + type: 'VC_REMOVED', + timestamp: Date.now(), + deviceName: '', + vcLabel: VCMetadata.fromVC(context.vcMetadata).id, + }), + { + to: context => context.serviceRefs.activityLog, + }, + ), + logWalletBindingSuccess: send( + (context: any) => + ActivityLogEvents.LOG_ACTIVITY({ + _vcKey: VCMetadata.fromVC(context.vcMetadata).getVcKey(), + type: 'WALLET_BINDING_SUCCESSFULL', + idType: getIdType(context.vcMetadata.issuer), + id: context.vcMetadata.id, + timestamp: Date.now(), + deviceName: '', + vcLabel: VCMetadata.fromVC(context.vcMetadata).id, + }), + { + to: context => context.serviceRefs.activityLog, + }, + ), + + logWalletBindingFailure: send( + (context: any) => + ActivityLogEvents.LOG_ACTIVITY({ + _vcKey: VCMetadata.fromVC(context.vcMetadata).getVcKey(), + type: 'WALLET_BINDING_FAILURE', + id: context.vcMetadata.id, + idType: getIdType(context.vcMetadata.issuer), + timestamp: Date.now(), + deviceName: '', + vcLabel: VCMetadata.fromVC(context.vcMetadata).id, + }), + { + to: context => context.serviceRefs.activityLog, + }, + ), + }; +}; diff --git a/machines/VerifiableCredential/VCItemMachine/VCItemEvents.ts b/machines/VerifiableCredential/VCItemMachine/VCItemEvents.ts new file mode 100644 index 00000000..54973840 --- /dev/null +++ b/machines/VerifiableCredential/VCItemMachine/VCItemEvents.ts @@ -0,0 +1,27 @@ +import {VCMetadata} from '../../../shared/VCMetadata'; +import {VC} from '../../../types/VC/vc'; + +export const VCItemEvents = { + DISMISS: () => ({}), + CREDENTIAL_DOWNLOADED: (response: VC) => ({response}), + STORE_RESPONSE: (response: VC) => ({response}), + STORE_ERROR: (error: Error) => ({error}), + POLL: () => ({}), + DOWNLOAD_READY: () => ({}), + FAILED: () => ({}), + GET_VC_RESPONSE: (response: VC) => ({response}), + INPUT_OTP: (OTP: string) => ({OTP}), + RESEND_OTP: () => ({}), + REFRESH: () => ({}), + ADD_WALLET_BINDING_ID: () => ({}), + CANCEL: () => ({}), + CONFIRM: () => ({}), + PIN_CARD: () => ({}), + KEBAB_POPUP: () => ({}), + SHOW_ACTIVITY: () => ({}), + CLOSE_VC_MODAL: () => ({}), + REMOVE: (vcMetadata: VCMetadata) => ({vcMetadata}), + UPDATE_VC_METADATA: (vcMetadata: VCMetadata) => ({vcMetadata}), + TAMPERED_VC: (key: string) => ({key}), + SHOW_BINDING_STATUS: () => ({}), +}; diff --git a/machines/VerifiableCredential/VCItemMachine/VCItemGaurds.ts b/machines/VerifiableCredential/VCItemMachine/VCItemGaurds.ts new file mode 100644 index 00000000..cab1f0de --- /dev/null +++ b/machines/VerifiableCredential/VCItemMachine/VCItemGaurds.ts @@ -0,0 +1,19 @@ +import {isSignedInResult} from '../../../shared/CloudBackupAndRestoreUtils'; +import {isHardwareKeystoreExists} from '../../../shared/cryptoutil/cryptoUtil'; + +export const VCItemGaurds = () => { + return { + hasCredential: (_, event) => { + const vc = event.response; + return vc?.credential != null || vc?.verifiableCredential != null; + }, + isSignedIn: (_context, event) => + (event.data as isSignedInResult).isSignedIn, + + isDownloadAllowed: _context => { + return _context.downloadCounter <= _context.maxDownloadCount; + }, + + isCustomSecureKeystore: () => isHardwareKeystoreExists, + }; +}; diff --git a/machines/VerifiableCredential/VCItemMachine/VCItemMachine.ts b/machines/VerifiableCredential/VCItemMachine/VCItemMachine.ts new file mode 100644 index 00000000..f2d41bbf --- /dev/null +++ b/machines/VerifiableCredential/VCItemMachine/VCItemMachine.ts @@ -0,0 +1,536 @@ +import {AppServices} from '../../../shared/GlobalContext'; +import {VCMetadata} from '../../../shared/VCMetadata'; +import {assign, EventFrom, send} from 'xstate'; +import {log} from 'xstate/lib/actions'; +import {VCItemActions} from './VCItemActions'; +import {VCItemGaurds} from './VCItemGaurds'; +import {VCItemServices} from './VCItemServices'; +import {VCItemModel} from './VCItemModel'; + +const machineName = 'vc-item-machine'; +const model = VCItemModel; +export const VCItemEvents = model.events; +export const VCItemMachine = model.createMachine( + { + predictableActionArguments: true, + preserveActionOrder: true, + tsTypes: {} as import('./VCItemMachine.typegen').Typegen0, + schema: { + context: model.initialContext, + events: {} as EventFrom, + }, + on: { + REFRESH: { + target: 'loadVc', + }, + UPDATE_VC_METADATA: { + actions: 'setVcMetadata', + }, + }, + description: + 'This machine is spawned for every VC downloaded, and tracks its lifecycle.', + id: machineName, + initial: 'loadVc', + states: { + loadVc: { + initial: 'loadVcFromContext', + states: { + loadVcFromContext: { + entry: ['requestVcContext'], + description: 'Fetch the VC data from the Memory.', + on: { + GET_VC_RESPONSE: [ + { + actions: ['setContext'], + cond: 'hasCredential', + target: `#vc-item-machine.idle`, + }, + { + target: 'loadVcFromStore', + }, + ], + }, + }, + loadVcFromStore: { + entry: 'requestStoredContext', + description: 'Check if VC data is in secured local storage.', + on: { + STORE_RESPONSE: [ + { + actions: ['setContext', 'storeVcInContext'], + cond: 'hasCredential', + target: '#vc-item-machine.idle', + }, + { + actions: 'addVcToInProgressDownloads', + target: 'loadVcFromServer', + }, + ], + TAMPERED_VC: { + actions: 'sendTamperedVc', + }, + }, + }, + loadVcFromServer: { + description: + "Download VC data from the server. Uses polling method to check when it's available.", + initial: 'loadDownloadLimitConfig', + states: { + loadDownloadLimitConfig: { + invoke: { + src: 'loadDownloadLimitConfig', + onDone: { + actions: ['setMaxDownloadCount', 'setDownloadInterval'], + target: 'verifyingDownloadLimitExpiry', + }, + }, + }, + verifyingDownloadLimitExpiry: { + entry: ['incrementDownloadCounter'], + invoke: { + src: 'checkDownloadExpiryLimit', + onDone: { + target: 'checkingStatus', + }, + onError: { + actions: [ + log((_, event) => (event.data as Error).message), + 'sendDownloadLimitExpire', + ], + }, + }, + }, + checkingStatus: { + invoke: { + src: 'checkStatus', + id: 'checkStatus', + }, + on: { + POLL: { + actions: send('POLL_STATUS', {to: 'checkStatus'}), + }, + DOWNLOAD_READY: { + target: 'downloadingCredential', + }, + FAILED: { + actions: 'sendDownloadLimitExpire', + }, + }, + }, + downloadingCredential: { + invoke: { + src: 'downloadCredential', + id: 'downloadCredential', + }, + on: { + POLL: [ + { + cond: 'isDownloadAllowed', + actions: [ + send('POLL_DOWNLOAD', {to: 'downloadCredential'}), + 'incrementDownloadCounter', + ], + }, + { + target: 'verifyingDownloadLimitExpiry', + }, + ], + CREDENTIAL_DOWNLOADED: { + actions: 'setCredential', + target: '#vc-item-machine.verifyingCredential', + }, + }, + }, + savingFailed: { + entry: ['removeVcMetaDataFromStorage'], + initial: 'idle', + states: { + idle: {}, + viewingVc: {}, + }, + on: { + DISMISS: { + actions: ['removeVcMetaDataFromVcMachineContext'], + target: '.viewingVc', + }, + }, + }, + }, + }, + }, + }, + walletBinding: { + initial: 'showBindingWarning', + states: { + showBindingWarning: { + on: { + CONFIRM: { + actions: 'sendActivationStartEvent', + target: 'requestingBindingOTP', + }, + CANCEL: [ + { + cond: context => context.isMachineInKebabPopupState, + target: '#vc-item-machine.kebabPopUp', + }, + { + target: '#vc-item-machine.idle', + }, + ], + }, + }, + requestingBindingOTP: { + invoke: { + src: 'requestBindingOTP', + onDone: [ + { + target: 'acceptingBindingOTP', + actions: ['setCommunicationDetails'], + }, + ], + onError: [ + { + actions: [ + 'setErrorAsWalletBindingError', + 'sendWalletBindingErrorEvent', + 'logWalletBindingFailure', + ], + target: 'showingWalletBindingError', + }, + ], + }, + }, + showingWalletBindingError: { + on: { + CANCEL: [ + { + cond: context => context.isMachineInKebabPopupState, + actions: ['unSetError'], + target: '#vc-item-machine.kebabPopUp', + }, + { + actions: ['unSetError'], + target: '#vc-item-machine.idle', + }, + ], + }, + }, + acceptingBindingOTP: { + entry: ['unSetOTP'], + on: { + INPUT_OTP: { + target: 'addKeyPair', + actions: ['setOTP'], + }, + DISMISS: [ + { + cond: context => context.isMachineInKebabPopupState, + target: '#vc-item-machine.kebabPopUp', + actions: [ + 'sendUserCancelledActivationFailedEndEvent', + 'unSetOTP', + 'unSetBindingTransactionId', + ], + }, + { + target: '#vc-item-machine.idle', + actions: [ + 'sendUserCancelledActivationFailedEndEvent', + 'unSetOTP', + 'unSetBindingTransactionId', + ], + }, + ], + RESEND_OTP: { + target: '.resendOTP', + }, + }, + initial: 'idle', + states: { + idle: {}, + resendOTP: { + invoke: { + src: 'requestBindingOTP', + onDone: { + target: '#vc-item-machine.idle', + actions: ['setCommunicationDetails'], + }, + onError: { + actions: [ + 'setErrorAsWalletBindingError', + 'sendWalletBindingErrorEvent', + ], + target: + '#vc-item-machine.walletBinding.showingWalletBindingError', + }, + }, + }, + }, + }, + addKeyPair: { + invoke: { + src: 'generateKeyPair', + onDone: [ + { + cond: 'isCustomSecureKeystore', + target: 'addingWalletBindingId', + actions: ['setPublicKey'], + }, + { + target: 'addingWalletBindingId', + actions: ['setPublicKey', 'setPrivateKey'], + }, + ], + onError: [ + { + actions: [ + 'setErrorAsWalletBindingError', + 'sendWalletBindingErrorEvent', + 'logWalletBindingFailure', + ], + target: 'showingWalletBindingError', + }, + ], + }, + }, + addingWalletBindingId: { + invoke: { + src: 'addWalletBindingId', + onDone: [ + { + cond: 'isCustomSecureKeystore', + target: 'updatingContextVariables', + actions: ['setWalletBindingResponse'], + }, + { + target: 'updatingPrivateKey', + /*The walletBindingResponse is used for conditional rendering in wallet binding. response and use it in updatingPrivateKey state*/ + actions: ['setWalletBindingResponse'], + }, + ], + onError: [ + { + actions: [ + 'setErrorAsWalletBindingError', + 'sendWalletBindingErrorEvent', + 'logWalletBindingFailure', + ], + target: 'showingWalletBindingError', + }, + ], + }, + }, + + updatingPrivateKey: { + invoke: { + src: 'updatePrivateKey', + onDone: { + target: 'updatingContextVariables', + }, + onError: { + actions: [ + 'setErrorAsWalletBindingError', + 'sendWalletBindingErrorEvent', + 'logWalletBindingFailure', + ], + target: 'showingWalletBindingError', + }, + }, + }, + updatingContextVariables: { + entry: [ + 'setThumbprintForWalletBindingId', + 'storeContext', + 'resetPrivateKey', + 'storeVcInContext', + 'unSetError', + 'sendActivationSuccessEvent', + 'logWalletBindingSuccess', + send('SHOW_BINDING_STATUS'), + ], + on: { + SHOW_BINDING_STATUS: [ + { + cond: context => context.isMachineInKebabPopupState, + actions: 'sendWalletBindingSuccess', + target: '#vc-item-machine.kebabPopUp', + }, + { + actions: 'sendWalletBindingSuccess', + target: '#vc-item-machine.idle', + }, + ], + }, + }, + }, + }, + kebabPopUp: { + entry: assign({isMachineInKebabPopupState: () => true}), + exit: assign({isMachineInKebabPopupState: () => false}), + on: { + DISMISS: { + actions: assign({ + isMachineInKebabPopupState: () => false, + }), + target: 'idle', + }, + ADD_WALLET_BINDING_ID: { + target: '#vc-item-machine.walletBinding', + }, + PIN_CARD: { + target: '.pinCard', + actions: 'setPinCard', + }, + SHOW_ACTIVITY: { + target: '#vc-item-machine.kebabPopUp.showActivities', + }, + REMOVE: { + actions: 'setVcKey', + target: '#vc-item-machine.kebabPopUp.removeWallet', + }, + CLOSE_VC_MODAL: { + actions: ['closeViewVcModal'], + target: '#vc-item-machine', + }, + }, + initial: 'idle', + states: { + idle: {}, + showActivities: { + on: { + DISMISS: '#vc-item-machine', + }, + }, + removeWallet: { + on: { + CONFIRM: { + target: 'removingVc', + }, + CANCEL: { + target: '#vc-item-machine', + }, + }, + }, + removingVc: { + entry: 'removeVcItem', + on: { + STORE_RESPONSE: { + actions: ['closeViewVcModal', 'refreshAllVcs', 'logRemovedVc'], + target: 'triggerAutoBackup', + }, + }, + }, + pinCard: { + entry: 'sendVcUpdated', + always: { + target: '#vc-item-machine.idle', + }, + }, + triggerAutoBackup: { + invoke: { + src: 'isUserSignedAlready', + onDone: [ + { + cond: 'isSignedIn', + actions: ['sendBackupEvent', 'refreshAllVcs', 'logRemovedVc'], + target: '#vc-item-machine', + }, + { + actions: ['refreshAllVcs', 'logRemovedVc'], + target: '#vc-item-machine', + }, + ], + }, + }, + }, + }, + verifyingCredential: { + invoke: { + src: 'verifyCredential', + onDone: { + actions: ['storeContext'], + }, + onError: { + //To-Do Handle Error Scenarios + actions: ['setErrorAsVerificationError'], + target: '.handleVCVerificationFailure', + }, + }, + on: { + STORE_RESPONSE: { + actions: [ + 'storeVcInContext', + 'logDownloaded', + 'sendTelemetryEvents', + 'removeVcFromInProgressDownloads', + ], + target: '.triggerAutoBackupForVcDownload', + }, + STORE_ERROR: { + target: '#vc-item-machine.loadVc.loadVcFromServer.savingFailed', + }, + }, + initial: 'idle', + states: { + idle: {}, + triggerAutoBackupForVcDownload: { + invoke: { + src: 'isUserSignedAlready', + onDone: [ + { + cond: 'isSignedIn', + actions: ['sendBackupEvent'], + target: '#vc-item-machine.idle', + }, + { + target: '#vc-item-machine.idle', + }, + ], + }, + }, + handleVCVerificationFailure: { + entry: ['removeVcMetaDataFromStorage'], + on: { + STORE_RESPONSE: { + actions: ['sendVerificationError'], + }, + }, + }, + }, + }, + idle: { + on: { + DISMISS: { + target: '#vc-item-machine.loadVc.loadVcFromContext', + }, + KEBAB_POPUP: { + target: 'kebabPopUp', + }, + ADD_WALLET_BINDING_ID: { + target: '#vc-item-machine.walletBinding', + }, + PIN_CARD: { + target: '#vc-item-machine.kebabPopUp.pinCard', + actions: 'setPinCard', + }, + }, + }, + }, + }, + { + actions: VCItemActions(model), + services: VCItemServices(model), + guards: VCItemGaurds(), + }, +); + +export const createVCItemMachine = ( + serviceRefs: AppServices, + vcMetadata: VCMetadata, +) => { + return VCItemMachine.withContext({ + ...VCItemMachine.context, + serviceRefs, + vcMetadata, + }); +}; diff --git a/machines/VerifiableCredential/VCItemMachine/VCItemMachine.typegen.ts b/machines/VerifiableCredential/VCItemMachine/VCItemMachine.typegen.ts new file mode 100644 index 00000000..cdd8aa9b --- /dev/null +++ b/machines/VerifiableCredential/VCItemMachine/VCItemMachine.typegen.ts @@ -0,0 +1,378 @@ +// This file was automatically generated. Edits will be overwritten + +export interface Typegen0 { + '@@xstate/typegen': true; + internalEvents: { + 'done.invoke.checkStatus': { + type: 'done.invoke.checkStatus'; + data: unknown; + __tip: 'See the XState TS docs to learn how to strongly type this.'; + }; + 'done.invoke.downloadCredential': { + type: 'done.invoke.downloadCredential'; + data: unknown; + __tip: 'See the XState TS docs to learn how to strongly type this.'; + }; + 'done.invoke.vc-item-machine.kebabPopUp.triggerAutoBackup:invocation[0]': { + type: 'done.invoke.vc-item-machine.kebabPopUp.triggerAutoBackup:invocation[0]'; + data: unknown; + __tip: 'See the XState TS docs to learn how to strongly type this.'; + }; + 'done.invoke.vc-item-machine.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]': { + type: 'done.invoke.vc-item-machine.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]'; + data: unknown; + __tip: 'See the XState TS docs to learn how to strongly type this.'; + }; + 'done.invoke.vc-item-machine.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]': { + type: 'done.invoke.vc-item-machine.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]'; + data: unknown; + __tip: 'See the XState TS docs to learn how to strongly type this.'; + }; + 'done.invoke.vc-item-machine.verifyingCredential.triggerAutoBackupForVcDownload:invocation[0]': { + type: 'done.invoke.vc-item-machine.verifyingCredential.triggerAutoBackupForVcDownload:invocation[0]'; + data: unknown; + __tip: 'See the XState TS docs to learn how to strongly type this.'; + }; + 'done.invoke.vc-item-machine.verifyingCredential:invocation[0]': { + type: 'done.invoke.vc-item-machine.verifyingCredential:invocation[0]'; + data: unknown; + __tip: 'See the XState TS docs to learn how to strongly type this.'; + }; + 'done.invoke.vc-item-machine.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]': { + type: 'done.invoke.vc-item-machine.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]'; + data: unknown; + __tip: 'See the XState TS docs to learn how to strongly type this.'; + }; + 'done.invoke.vc-item-machine.walletBinding.addKeyPair:invocation[0]': { + type: 'done.invoke.vc-item-machine.walletBinding.addKeyPair:invocation[0]'; + data: unknown; + __tip: 'See the XState TS docs to learn how to strongly type this.'; + }; + 'done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]': { + type: 'done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]'; + data: unknown; + __tip: 'See the XState TS docs to learn how to strongly type this.'; + }; + 'done.invoke.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]': { + type: 'done.invoke.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]'; + data: unknown; + __tip: 'See the XState TS docs to learn how to strongly type this.'; + }; + 'done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]': { + type: 'done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]'; + data: unknown; + __tip: 'See the XState TS docs to learn how to strongly type this.'; + }; + 'error.platform.checkStatus': { + type: 'error.platform.checkStatus'; + data: unknown; + }; + 'error.platform.downloadCredential': { + type: 'error.platform.downloadCredential'; + data: unknown; + }; + 'error.platform.vc-item-machine.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]': { + type: 'error.platform.vc-item-machine.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]'; + data: unknown; + }; + 'error.platform.vc-item-machine.verifyingCredential:invocation[0]': { + type: 'error.platform.vc-item-machine.verifyingCredential:invocation[0]'; + data: unknown; + }; + 'error.platform.vc-item-machine.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]': { + type: 'error.platform.vc-item-machine.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]'; + data: unknown; + }; + 'error.platform.vc-item-machine.walletBinding.addKeyPair:invocation[0]': { + type: 'error.platform.vc-item-machine.walletBinding.addKeyPair:invocation[0]'; + data: unknown; + }; + 'error.platform.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]': { + type: 'error.platform.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]'; + data: unknown; + }; + 'error.platform.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]': { + type: 'error.platform.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]'; + data: unknown; + }; + 'error.platform.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]': { + type: 'error.platform.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]'; + data: unknown; + }; + 'xstate.init': {type: 'xstate.init'}; + }; + invokeSrcNameMap: { + addWalletBindingId: 'done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]'; + checkDownloadExpiryLimit: 'done.invoke.vc-item-machine.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]'; + checkStatus: 'done.invoke.checkStatus'; + downloadCredential: 'done.invoke.downloadCredential'; + generateKeyPair: 'done.invoke.vc-item-machine.walletBinding.addKeyPair:invocation[0]'; + isUserSignedAlready: + | 'done.invoke.vc-item-machine.kebabPopUp.triggerAutoBackup:invocation[0]' + | 'done.invoke.vc-item-machine.verifyingCredential.triggerAutoBackupForVcDownload:invocation[0]'; + loadDownloadLimitConfig: 'done.invoke.vc-item-machine.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]'; + requestBindingOTP: + | 'done.invoke.vc-item-machine.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]' + | 'done.invoke.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]'; + updatePrivateKey: 'done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]'; + verifyCredential: 'done.invoke.vc-item-machine.verifyingCredential:invocation[0]'; + }; + missingImplementations: { + actions: + | 'addVcToInProgressDownloads' + | 'closeViewVcModal' + | 'incrementDownloadCounter' + | 'logDownloaded' + | 'logRemovedVc' + | 'logWalletBindingFailure' + | 'logWalletBindingSuccess' + | 'refreshAllVcs' + | 'removeVcFromInProgressDownloads' + | 'removeVcItem' + | 'removeVcMetaDataFromStorage' + | 'removeVcMetaDataFromVcMachineContext' + | 'requestStoredContext' + | 'requestVcContext' + | 'resetPrivateKey' + | 'sendActivationStartEvent' + | 'sendActivationSuccessEvent' + | 'sendBackupEvent' + | 'sendDownloadLimitExpire' + | 'sendTamperedVc' + | 'sendTelemetryEvents' + | 'sendUserCancelledActivationFailedEndEvent' + | 'sendVcUpdated' + | 'sendVerificationError' + | 'sendWalletBindingErrorEvent' + | 'sendWalletBindingSuccess' + | 'setCommunicationDetails' + | 'setContext' + | 'setCredential' + | 'setDownloadInterval' + | 'setErrorAsVerificationError' + | 'setErrorAsWalletBindingError' + | 'setMaxDownloadCount' + | 'setOTP' + | 'setPinCard' + | 'setPrivateKey' + | 'setPublicKey' + | 'setThumbprintForWalletBindingId' + | 'setVcKey' + | 'setVcMetadata' + | 'setWalletBindingResponse' + | 'storeContext' + | 'storeVcInContext' + | 'unSetBindingTransactionId' + | 'unSetError' + | 'unSetOTP'; + delays: never; + guards: + | 'hasCredential' + | 'isCustomSecureKeystore' + | 'isDownloadAllowed' + | 'isSignedIn'; + services: + | 'addWalletBindingId' + | 'checkDownloadExpiryLimit' + | 'checkStatus' + | 'downloadCredential' + | 'generateKeyPair' + | 'isUserSignedAlready' + | 'loadDownloadLimitConfig' + | 'requestBindingOTP' + | 'updatePrivateKey' + | 'verifyCredential'; + }; + eventsCausingActions: { + addVcToInProgressDownloads: 'STORE_RESPONSE'; + closeViewVcModal: 'CLOSE_VC_MODAL' | 'STORE_RESPONSE'; + incrementDownloadCounter: + | 'POLL' + | 'done.invoke.vc-item-machine.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]'; + logDownloaded: 'STORE_RESPONSE'; + logRemovedVc: + | 'STORE_RESPONSE' + | 'done.invoke.vc-item-machine.kebabPopUp.triggerAutoBackup:invocation[0]'; + logWalletBindingFailure: + | 'error.platform.vc-item-machine.walletBinding.addKeyPair:invocation[0]' + | 'error.platform.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]' + | 'error.platform.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]' + | 'error.platform.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]'; + logWalletBindingSuccess: + | 'done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]' + | 'done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]'; + refreshAllVcs: + | 'STORE_RESPONSE' + | 'done.invoke.vc-item-machine.kebabPopUp.triggerAutoBackup:invocation[0]'; + removeVcFromInProgressDownloads: 'STORE_RESPONSE'; + removeVcItem: 'CONFIRM'; + removeVcMetaDataFromStorage: + | 'STORE_ERROR' + | 'error.platform.vc-item-machine.verifyingCredential:invocation[0]'; + removeVcMetaDataFromVcMachineContext: 'DISMISS'; + requestStoredContext: 'GET_VC_RESPONSE'; + requestVcContext: 'DISMISS' | 'REFRESH' | 'STORE_ERROR' | 'xstate.init'; + resetPrivateKey: + | 'done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]' + | 'done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]'; + sendActivationStartEvent: 'CONFIRM'; + sendActivationSuccessEvent: + | 'done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]' + | 'done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]'; + sendBackupEvent: + | 'done.invoke.vc-item-machine.kebabPopUp.triggerAutoBackup:invocation[0]' + | 'done.invoke.vc-item-machine.verifyingCredential.triggerAutoBackupForVcDownload:invocation[0]'; + sendDownloadLimitExpire: + | 'FAILED' + | 'error.platform.vc-item-machine.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]'; + sendTamperedVc: 'TAMPERED_VC'; + sendTelemetryEvents: 'STORE_RESPONSE'; + sendUserCancelledActivationFailedEndEvent: 'DISMISS'; + sendVcUpdated: 'PIN_CARD'; + sendVerificationError: 'STORE_RESPONSE'; + sendWalletBindingErrorEvent: + | 'error.platform.vc-item-machine.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]' + | 'error.platform.vc-item-machine.walletBinding.addKeyPair:invocation[0]' + | 'error.platform.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]' + | 'error.platform.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]' + | 'error.platform.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]'; + sendWalletBindingSuccess: 'SHOW_BINDING_STATUS'; + setCommunicationDetails: + | 'done.invoke.vc-item-machine.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]' + | 'done.invoke.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]'; + setContext: 'GET_VC_RESPONSE' | 'STORE_RESPONSE'; + setCredential: 'CREDENTIAL_DOWNLOADED'; + setDownloadInterval: 'done.invoke.vc-item-machine.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]'; + setErrorAsVerificationError: 'error.platform.vc-item-machine.verifyingCredential:invocation[0]'; + setErrorAsWalletBindingError: + | 'error.platform.vc-item-machine.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]' + | 'error.platform.vc-item-machine.walletBinding.addKeyPair:invocation[0]' + | 'error.platform.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]' + | 'error.platform.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]' + | 'error.platform.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]'; + setMaxDownloadCount: 'done.invoke.vc-item-machine.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]'; + setOTP: 'INPUT_OTP'; + setPinCard: 'PIN_CARD'; + setPrivateKey: 'done.invoke.vc-item-machine.walletBinding.addKeyPair:invocation[0]'; + setPublicKey: 'done.invoke.vc-item-machine.walletBinding.addKeyPair:invocation[0]'; + setThumbprintForWalletBindingId: + | 'done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]' + | 'done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]'; + setVcKey: 'REMOVE'; + setVcMetadata: 'UPDATE_VC_METADATA'; + setWalletBindingResponse: 'done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]'; + storeContext: + | 'done.invoke.vc-item-machine.verifyingCredential:invocation[0]' + | 'done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]' + | 'done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]'; + storeVcInContext: + | 'STORE_RESPONSE' + | 'done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]' + | 'done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]'; + unSetBindingTransactionId: 'DISMISS'; + unSetError: + | 'CANCEL' + | 'done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]' + | 'done.invoke.vc-item-machine.walletBinding.updatingPrivateKey:invocation[0]'; + unSetOTP: + | 'DISMISS' + | 'done.invoke.vc-item-machine.walletBinding.requestingBindingOTP:invocation[0]'; + }; + eventsCausingDelays: {}; + eventsCausingGuards: { + hasCredential: 'GET_VC_RESPONSE' | 'STORE_RESPONSE'; + isCustomSecureKeystore: + | 'done.invoke.vc-item-machine.walletBinding.addKeyPair:invocation[0]' + | 'done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]'; + isDownloadAllowed: 'POLL'; + isSignedIn: + | 'done.invoke.vc-item-machine.kebabPopUp.triggerAutoBackup:invocation[0]' + | 'done.invoke.vc-item-machine.verifyingCredential.triggerAutoBackupForVcDownload:invocation[0]'; + }; + eventsCausingServices: { + addWalletBindingId: 'done.invoke.vc-item-machine.walletBinding.addKeyPair:invocation[0]'; + checkDownloadExpiryLimit: + | 'POLL' + | 'done.invoke.vc-item-machine.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]'; + checkStatus: 'done.invoke.vc-item-machine.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]'; + downloadCredential: 'DOWNLOAD_READY'; + generateKeyPair: 'INPUT_OTP'; + isUserSignedAlready: 'STORE_RESPONSE'; + loadDownloadLimitConfig: 'STORE_ERROR' | 'STORE_RESPONSE'; + requestBindingOTP: 'CONFIRM' | 'RESEND_OTP'; + updatePrivateKey: 'done.invoke.vc-item-machine.walletBinding.addingWalletBindingId:invocation[0]'; + verifyCredential: 'CREDENTIAL_DOWNLOADED'; + }; + matchesStates: + | 'idle' + | 'kebabPopUp' + | 'kebabPopUp.idle' + | 'kebabPopUp.pinCard' + | 'kebabPopUp.removeWallet' + | 'kebabPopUp.removingVc' + | 'kebabPopUp.showActivities' + | 'kebabPopUp.triggerAutoBackup' + | 'loadVc' + | 'loadVc.loadVcFromContext' + | 'loadVc.loadVcFromServer' + | 'loadVc.loadVcFromServer.checkingStatus' + | 'loadVc.loadVcFromServer.downloadingCredential' + | 'loadVc.loadVcFromServer.loadDownloadLimitConfig' + | 'loadVc.loadVcFromServer.savingFailed' + | 'loadVc.loadVcFromServer.savingFailed.idle' + | 'loadVc.loadVcFromServer.savingFailed.viewingVc' + | 'loadVc.loadVcFromServer.verifyingDownloadLimitExpiry' + | 'loadVc.loadVcFromStore' + | 'verifyingCredential' + | 'verifyingCredential.handleVCVerificationFailure' + | 'verifyingCredential.idle' + | 'verifyingCredential.triggerAutoBackupForVcDownload' + | 'walletBinding' + | 'walletBinding.acceptingBindingOTP' + | 'walletBinding.acceptingBindingOTP.idle' + | 'walletBinding.acceptingBindingOTP.resendOTP' + | 'walletBinding.addKeyPair' + | 'walletBinding.addingWalletBindingId' + | 'walletBinding.requestingBindingOTP' + | 'walletBinding.showBindingWarning' + | 'walletBinding.showingWalletBindingError' + | 'walletBinding.updatingContextVariables' + | 'walletBinding.updatingPrivateKey' + | { + kebabPopUp?: + | 'idle' + | 'pinCard' + | 'removeWallet' + | 'removingVc' + | 'showActivities' + | 'triggerAutoBackup'; + loadVc?: + | 'loadVcFromContext' + | 'loadVcFromServer' + | 'loadVcFromStore' + | { + loadVcFromServer?: + | 'checkingStatus' + | 'downloadingCredential' + | 'loadDownloadLimitConfig' + | 'savingFailed' + | 'verifyingDownloadLimitExpiry' + | {savingFailed?: 'idle' | 'viewingVc'}; + }; + verifyingCredential?: + | 'handleVCVerificationFailure' + | 'idle' + | 'triggerAutoBackupForVcDownload'; + walletBinding?: + | 'acceptingBindingOTP' + | 'addKeyPair' + | 'addingWalletBindingId' + | 'requestingBindingOTP' + | 'showBindingWarning' + | 'showingWalletBindingError' + | 'updatingContextVariables' + | 'updatingPrivateKey' + | {acceptingBindingOTP?: 'idle' | 'resendOTP'}; + }; + tags: never; +} diff --git a/machines/VerifiableCredential/VCItemMachine/VCItemModel.ts b/machines/VerifiableCredential/VCItemMachine/VCItemModel.ts new file mode 100644 index 00000000..7d85ac94 --- /dev/null +++ b/machines/VerifiableCredential/VCItemMachine/VCItemModel.ts @@ -0,0 +1,34 @@ +import {createModel} from 'xstate/lib/model'; +import {AppServices} from '../../../shared/GlobalContext'; +import {VCMetadata} from '../../../shared/VCMetadata'; +import { + VerifiableCredential, + WalletBindingResponse, +} from '../../../types/VC/vc'; +import {CommunicationDetails} from '../../../shared/Utils'; +import {VCItemEvents} from './VCItemEvents'; + +export const VCItemModel = createModel( + { + serviceRefs: {} as AppServices, + vcMetadata: {} as VCMetadata, + generatedOn: new Date() as Date, + verifiableCredential: null as unknown as VerifiableCredential, + hashedId: '', + publicKey: '', + privateKey: '', + OTP: '', + error: '', + bindingTransactionId: '', + requestId: '', + downloadCounter: 0, + maxDownloadCount: null as unknown as number, + downloadInterval: null as unknown as number, + walletBindingResponse: null as unknown as WalletBindingResponse, + isMachineInKebabPopupState: false, + communicationDetails: null as unknown as CommunicationDetails, + }, + { + events: VCItemEvents, + }, +); diff --git a/machines/VerifiableCredential/VCItemMachine/VCItemSelectors.ts b/machines/VerifiableCredential/VCItemMachine/VCItemSelectors.ts new file mode 100644 index 00000000..90e0e77e --- /dev/null +++ b/machines/VerifiableCredential/VCItemMachine/VCItemSelectors.ts @@ -0,0 +1,114 @@ +import {StateFrom} from 'xstate'; +import {VCMetadata} from '../../../shared/VCMetadata'; +import {VCItemMachine} from './VCItemMachine'; +import {getMosipLogo} from '../../../components/VC/common/VCUtils'; + +type State = StateFrom; + +export function selectVerifiableCredential(state: State) { + return state.context.verifiableCredential; +} + +export function selectCredential(state: State) { + return new VCMetadata(state.context.vcMetadata).isFromOpenId4VCI() + ? state.context.verifiableCredential?.credential + : state.context.verifiableCredential; +} + +export function selectVerifiableCredentialData(state: State) { + const vcMetadata = new VCMetadata(state.context.vcMetadata); + return vcMetadata.isFromOpenId4VCI() + ? { + vcMetadata: vcMetadata, + face: state.context.verifiableCredential?.credential?.credentialSubject + .face, + issuerLogo: state.context.verifiableCredential?.issuerLogo, + wellKnown: state.context.verifiableCredential?.wellKnown, + credentialTypes: state.context.verifiableCredential?.credentialTypes, + issuer: vcMetadata.issuer, + } + : { + vcMetadata: vcMetadata, + issuer: vcMetadata.issuer, + face: state.context.credential?.biometrics?.face, + issuerLogo: getMosipLogo(), + }; +} + +export function selectKebabPopUp(state: State) { + return state.context.isMachineInKebabPopupState; +} + +export function selectContext(state: State) { + return state.context; +} + +export function selectGeneratedOn(state: State) { + return state.context.generatedOn; +} + +export function selectWalletBindingSuccess(state: State) { + return state.context.walletBindingResponse; +} + +export function selectWalletBindingResponse(state: State) { + return state.context.walletBindingResponse; +} + +export function selectIsCommunicationDetails(state: State) { + return state.context.communicationDetails; +} + +export function selectWalletBindingError(state: State) { + return state.context.error; +} + +export function selectBindingAuthFailedError(state: State) { + return state.context.error; +} + +export function selectAcceptingBindingOtp(state: State) { + return state.matches('walletBinding.acceptingBindingOTP'); +} + +export function selectWalletBindingInProgress(state: State) { + return ( + state.matches('walletBinding.requestingBindingOTP') || + state.matches('walletBinding.addingWalletBindingId') || + state.matches('walletBinding.addKeyPair') || + state.matches('walletBinding.updatingPrivateKey') + ); +} + +export function selectBindingWarning(state: State) { + return state.matches('walletBinding.showBindingWarning'); +} + +export function selectRemoveWalletWarning(state: State) { + return state.matches('kebabPopUp.removeWallet'); +} + +export function selectIsPinned(state: State) { + return state.context.vcMetadata.isPinned; +} + +export function selectOtpError(state: State) { + return state.context.error; +} + +export function selectShowActivities(state: State) { + return state.matches('kebabPopUp.showActivities'); +} + +export function selectShowWalletBindingError(state: State) { + return state.matches('walletBinding.showingWalletBindingError'); +} + +export function selectVc(state: State) { + const {serviceRefs, ...data} = state.context; + return data; +} + +export function selectId(state: State) { + return state.context.vcMetadata.id; +} diff --git a/machines/VerifiableCredential/VCItemMachine/VCItemServices.ts b/machines/VerifiableCredential/VCItemMachine/VCItemServices.ts new file mode 100644 index 00000000..596f65b0 --- /dev/null +++ b/machines/VerifiableCredential/VCItemMachine/VCItemServices.ts @@ -0,0 +1,208 @@ +import SecureKeystore from '@mosip/secure-keystore'; +import Cloud from '../../../shared/CloudBackupAndRestoreUtils'; +import {VCMetadata} from '../../../shared/VCMetadata'; +import getAllConfigurations, { + API_URLS, + DownloadProps, +} from '../../../shared/api'; +import { + generateKeys, + isHardwareKeystoreExists, +} from '../../../shared/cryptoutil/cryptoUtil'; +import { + getBindingCertificateConstant, + savePrivateKey, +} from '../../../shared/keystore/SecureKeystore'; +import {CredentialDownloadResponse, request} from '../../../shared/request'; +import {WalletBindingResponse} from '../../../types/VC/vc'; +import {verifyCredential} from '../../../shared/vcjs/verifyCredential'; +import {getMosipIdentifier} from '../../../shared/commonUtil'; + +export const VCItemServices = model => { + return { + isUserSignedAlready: () => async () => { + return await Cloud.isSignedInAlready(); + }, + + updatePrivateKey: async context => { + const hasSetPrivateKey: boolean = await savePrivateKey( + context.walletBindingResponse.walletBindingId, + context.privateKey, + ); + if (!hasSetPrivateKey) { + throw new Error('Could not store private key in keystore.'); + } + return ''; + }, + loadDownloadLimitConfig: async context => { + var resp = await getAllConfigurations(); + const maxLimit: number = resp.vcDownloadMaxRetry; + const vcDownloadPoolInterval: number = resp.vcDownloadPoolInterval; + + const downloadProps: DownloadProps = { + maxDownloadLimit: maxLimit, + downloadInterval: vcDownloadPoolInterval, + }; + return downloadProps; + }, + + checkDownloadExpiryLimit: async context => { + if (context.downloadCounter > context.maxDownloadCount) { + throw new Error( + 'Download limit expired for request id: ' + + context.vcMetadata.requestId, + ); + } + }, + addWalletBindingId: async context => { + const response = await request( + API_URLS.walletBinding.method, + API_URLS.walletBinding.buildURL(), + { + requestTime: String(new Date().toISOString()), + request: { + authFactorType: 'WLA', + format: 'jwt', + individualId: VCMetadata.fromVC(context.vcMetadata).id, + transactionId: context.bindingTransactionId, + publicKey: context.publicKey, + challengeList: [ + { + authFactorType: 'OTP', + challenge: context.OTP, + format: 'alpha-numeric', + }, + ], + }, + }, + ); + const certificate = response.response.certificate; + await savePrivateKey( + getBindingCertificateConstant(VCMetadata.fromVC(context.vcMetadata).id), + certificate, + ); + + const walletResponse: WalletBindingResponse = { + walletBindingId: response.response.encryptedWalletBindingId, + keyId: response.response.keyId, + thumbprint: response.response.thumbprint, + expireDateTime: response.response.expireDateTime, + }; + return walletResponse; + }, + + generateKeyPair: async context => { + if (!isHardwareKeystoreExists) { + return await generateKeys(); + } + const isBiometricsEnabled = SecureKeystore.hasBiometricsEnabled(); + return SecureKeystore.generateKeyPair( + VCMetadata.fromVC(context.vcMetadata).id, + isBiometricsEnabled, + 0, + ); + }, + requestBindingOTP: async context => { + const vc = VCMetadata.fromVC(context.vcMetadata).isFromOpenId4VCI() + ? context.verifiableCredential.credential + : context.verifiableCredential; + const response = await request( + API_URLS.bindingOtp.method, + API_URLS.bindingOtp.buildURL(), + { + requestTime: String(new Date().toISOString()), + request: { + individualId: getMosipIdentifier(vc.credentialSubject), + otpChannels: ['EMAIL', 'PHONE'], + }, + }, + ); + if (response.response == null) { + throw new Error('Could not process request'); + } + return response; + }, + checkStatus: context => (callback, onReceive) => { + const pollInterval = setInterval( + () => callback(model.events.POLL()), + context.downloadInterval, + ); + + onReceive(async event => { + if (event.type === 'POLL_STATUS') { + try { + const response = await request( + API_URLS.credentialStatus.method, + API_URLS.credentialStatus.buildURL(context.vcMetadata.requestId), + ); + switch (response.response?.statusCode) { + case 'NEW': + break; + case 'ISSUED': + case 'printing': + callback(model.events.DOWNLOAD_READY()); + break; + case 'FAILED': + default: + callback(model.events.FAILED()); + clearInterval(pollInterval); + break; + } + } catch (error) { + callback(model.events.FAILED()); + clearInterval(pollInterval); + } + } + }); + + return () => clearInterval(pollInterval); + }, + + downloadCredential: context => (callback, onReceive) => { + const pollInterval = setInterval( + () => callback(model.events.POLL()), + context.downloadInterval, + ); + + onReceive(async event => { + if (event.type === 'POLL_DOWNLOAD') { + const response: CredentialDownloadResponse = await request( + API_URLS.credentialDownload.method, + API_URLS.credentialDownload.buildURL(), + { + individualId: context.vcMetadata.id, + requestId: context.vcMetadata.requestId, + }, + ); + + callback( + model.events.CREDENTIAL_DOWNLOADED({ + credential: response.credential, + verifiableCredential: response.verifiableCredential, + generatedOn: new Date(), + id: context.vcMetadata.id, + idType: context.vcMetadata.idType, + requestId: context.vcMetadata.requestId, + lastVerifiedOn: null, + walletBindingResponse: null, + credentialRegistry: '', + }), + ); + } + }); + + return () => clearInterval(pollInterval); + }, + + verifyCredential: async context => { + if (context.verifiableCredential) { + const verificationResult = await verifyCredential( + context.verifiableCredential, + ); + if (!verificationResult.isVerified) { + throw new Error(verificationResult.errorMessage); + } + } + }, + }; +}; diff --git a/machines/VCItemMachine/vc.ts b/machines/VerifiableCredential/VCMetaMachine/vc.ts similarity index 88% rename from machines/VCItemMachine/vc.ts rename to machines/VerifiableCredential/VCMetaMachine/vc.ts index 4356bdbd..c6b30dc1 100644 --- a/machines/VCItemMachine/vc.ts +++ b/machines/VerifiableCredential/VCMetaMachine/vc.ts @@ -1,15 +1,20 @@ import {EventFrom, send, sendParent, StateFrom} from 'xstate'; import {createModel} from 'xstate/lib/model'; -import {StoreEvents} from '../store'; -import {VC} from '../../types/VC/ExistingMosipVC/vc'; -import {AppServices} from '../../shared/GlobalContext'; +import {StoreEvents} from '../../store'; +import {VC} from '../../../types/VC/vc'; +import {AppServices} from '../../../shared/GlobalContext'; import {log, respond} from 'xstate/lib/actions'; -import {MY_VCS_STORE_KEY, RECEIVED_VCS_STORE_KEY} from '../../shared/constants'; -import {parseMetadatas, VCMetadata} from '../../shared/VCMetadata'; -import {ActivityLogEvents} from '../activityLog'; -import {ActivityLog} from '../../components/ActivityLogEvent'; -import Cloud, {isSignedInResult} from '../../shared/CloudBackupAndRestoreUtils'; -import {BackupEvents} from '../backupAndRestore/backup'; +import { + MY_VCS_STORE_KEY, + RECEIVED_VCS_STORE_KEY, +} from '../../../shared/constants'; +import {parseMetadatas, VCMetadata} from '../../../shared/VCMetadata'; +import {ActivityLogEvents} from '../../activityLog'; +import {ActivityLog} from '../../../components/ActivityLogEvent'; +import Cloud, { + isSignedInResult, +} from '../../../shared/CloudBackupAndRestoreUtils'; +import {BackupEvents} from '../../backupAndRestore/backup'; const model = createModel( { @@ -17,11 +22,10 @@ const model = createModel( myVcs: [] as VCMetadata[], receivedVcs: [] as VCMetadata[], vcs: {} as Record, - inProgressVcDownloads: new Set(), - areAllVcsDownloaded: false as boolean, + inProgressVcDownloads: new Set(), //VCDownloadInProgress walletBindingSuccess: false, tamperedVcs: [] as VCMetadata[], - downloadingFailedVcs: [] as VCMetadata[], + downloadingFailedVcs: [] as VCMetadata[], //VCDownloadFailed verificationErrorMessage: '' as string, }, { @@ -33,22 +37,20 @@ const model = createModel( VC_ADDED: (vcMetadata: VCMetadata) => ({vcMetadata}), REMOVE_VC_FROM_CONTEXT: (vcMetadata: VCMetadata) => ({vcMetadata}), VC_METADATA_UPDATED: (vcMetadata: VCMetadata) => ({vcMetadata}), - VC_DOWNLOADED: (vc: VC) => ({vc}), - VC_DOWNLOADED_FROM_OPENID4VCI: (vc: VC, vcMetadata: VCMetadata) => ({ + VC_DOWNLOADED: (vc: VC, vcMetadata?: VCMetadata) => ({ vc, vcMetadata, }), REFRESH_MY_VCS: () => ({}), REFRESH_MY_VCS_TWO: (vc: VC) => ({vc}), REFRESH_RECEIVED_VCS: () => ({}), - GET_RECEIVED_VCS: () => ({}), WALLET_BINDING_SUCCESS: () => ({}), RESET_WALLET_BINDING_SUCCESS: () => ({}), ADD_VC_TO_IN_PROGRESS_DOWNLOADS: (requestId: string) => ({requestId}), REMOVE_VC_FROM_IN_PROGRESS_DOWNLOADS: (vcMetadata: VCMetadata) => ({ vcMetadata, }), - RESET_ARE_ALL_VCS_DOWNLOADED: () => ({}), + RESET_IN_PROGRESS_VCS_DOWNLOADED: () => ({}), TAMPERED_VC: (VC: VCMetadata) => ({VC}), REMOVE_TAMPERED_VCS: () => ({}), DOWNLOAD_LIMIT_EXPIRED: (vcMetadata: VCMetadata) => ({vcMetadata}), @@ -174,11 +176,8 @@ export const vcMachine = REMOVE_VC_FROM_IN_PROGRESS_DOWNLOADS: { actions: 'removeVcFromInProgressDownlods', }, - RESET_ARE_ALL_VCS_DOWNLOADED: { - actions: 'resetAreAllVcsDownloaded', - }, - VC_DOWNLOADED_FROM_OPENID4VCI: { - actions: 'setDownloadedVCFromOpenId4VCI', + RESET_IN_PROGRESS_VCS_DOWNLOADED: { + actions: 'resetInProgressVcsDownloaded', }, RESET_WALLET_BINDING_SUCCESS: { actions: 'resetWalletBindingSuccess', @@ -266,17 +265,11 @@ export const vcMachine = to: context => context.serviceRefs.backup, }), - getReceivedVcsResponse: respond(context => ({ - type: 'VC_RESPONSE', - response: context.receivedVcs || [], - })), - getVcItemResponse: respond((context, event) => { - const vc = - context.vcs[VCMetadata.fromVC(event.vcMetadata)?.getVcKey()]; return { type: 'GET_VC_RESPONSE', - vc: vc, + response: + context.vcs[VCMetadata.fromVC(event.vcMetadata)?.getVcKey()], }; }), @@ -324,7 +317,8 @@ export const vcMachine = }), setDownloadedVc: (context, event) => { - const vcUniqueId = VCMetadata.fromVC(event.vc).getVcKey(); + const vcMetaData = event.vcMetadata ? event.vcMetadata : event.vc; + const vcUniqueId = VCMetadata.fromVC(vcMetaData).getVcKey(); context.vcs[vcUniqueId] = event.vc; }, @@ -350,23 +344,11 @@ export const vcMachine = return updatedInProgressList; }, - areAllVcsDownloaded: context => { - if (context.inProgressVcDownloads.size == 0) { - return true; - } - return false; - }, }), - resetAreAllVcsDownloaded: model.assign({ - areAllVcsDownloaded: () => false, + resetInProgressVcsDownloaded: model.assign({ inProgressVcDownloads: new Set(), }), - setDownloadedVCFromOpenId4VCI: (context, event) => { - if (event.vc) - context.vcs[VCMetadata.fromVC(event.vcMetadata).getVcKey()] = - event.vc; - }, setUpdatedVcMetadatas: send( _context => { @@ -488,10 +470,6 @@ export function selectBindedVcsMetadata(state: State): VCMetadata[] { }); } -export function selectAreAllVcsDownloaded(state: State) { - return state.context.areAllVcsDownloaded; -} - export function selectInProgressVcDownloads(state: State) { return state.context.inProgressVcDownloads; } diff --git a/machines/VCItemMachine/vc.typegen.ts b/machines/VerifiableCredential/VCMetaMachine/vc.typegen.ts similarity index 94% rename from machines/VCItemMachine/vc.typegen.ts rename to machines/VerifiableCredential/VCMetaMachine/vc.typegen.ts index 613d46c9..2e41d7c4 100644 --- a/machines/VCItemMachine/vc.typegen.ts +++ b/machines/VerifiableCredential/VCMetaMachine/vc.typegen.ts @@ -1,3 +1,5 @@ +// This file was automatically generated. Edits will be overwritten + export interface Typegen0 { '@@xstate/typegen': true; internalEvents: { @@ -37,12 +39,11 @@ export interface Typegen0 { | 'REMOVE_VC_FROM_IN_PROGRESS_DOWNLOADS' | 'VERIFY_VC_FAILED'; removeVcFromMyVcs: 'REMOVE_VC_FROM_CONTEXT'; - resetAreAllVcsDownloaded: 'RESET_ARE_ALL_VCS_DOWNLOADED'; resetDownloadFailedVcs: 'STORE_RESPONSE'; + resetInProgressVcsDownloaded: 'RESET_IN_PROGRESS_VCS_DOWNLOADED'; resetVerificationErrorMessage: 'RESET_VERIFY_ERROR'; resetWalletBindingSuccess: 'RESET_WALLET_BINDING_SUCCESS'; sendBackupEvent: 'done.invoke.vc.tamperedVCs.triggerAutoBackupForTamperedVcDeletion:invocation[0]'; - setDownloadedVCFromOpenId4VCI: 'VC_DOWNLOADED_FROM_OPENID4VCI'; setDownloadedVc: 'VC_DOWNLOADED'; setDownloadingFailedVcs: 'DOWNLOAD_LIMIT_EXPIRED'; setMyVcs: 'STORE_RESPONSE'; @@ -73,6 +74,7 @@ export interface Typegen0 { | 'ready.receivedVcs.idle' | 'ready.receivedVcs.refreshing' | 'tamperedVCs' + | 'tamperedVCs.idle' | 'tamperedVCs.refreshVcsMetadata' | 'tamperedVCs.triggerAutoBackupForTamperedVcDeletion' | { @@ -85,6 +87,7 @@ export interface Typegen0 { receivedVcs?: 'idle' | 'refreshing'; }; tamperedVCs?: + | 'idle' | 'refreshVcsMetadata' | 'triggerAutoBackupForTamperedVcDeletion'; }; diff --git a/machines/app.ts b/machines/app.ts index 54029d80..f7fca8c1 100644 --- a/machines/app.ts +++ b/machines/app.ts @@ -6,7 +6,10 @@ import {createModel} from 'xstate/lib/model'; import {authMachine, createAuthMachine} from './auth'; import {createSettingsMachine, settingsMachine} from './settings'; import {StoreEvents, storeMachine} from './store'; -import {createVcMachine, vcMachine} from './VCItemMachine/vc'; +import { + createVcMachine, + vcMachine, +} from './VerifiableCredential/VCMetaMachine/vc'; import {activityLogMachine, createActivityLogMachine} from './activityLog'; import { createRequestMachine, diff --git a/machines/auth.typegen.ts b/machines/auth.typegen.ts index 71a4e6dc..5ad8714e 100644 --- a/machines/auth.typegen.ts +++ b/machines/auth.typegen.ts @@ -30,6 +30,7 @@ export interface Typegen0 { requestStoredContext: 'xstate.init'; setBiometrics: 'SETUP_BIOMETRICS'; setContext: 'STORE_RESPONSE'; + setIsToggleFromSettings: 'CHANGE_METHOD'; setLanguage: 'SETUP_BIOMETRICS' | 'SETUP_PASSCODE'; setPasscode: 'SETUP_PASSCODE'; setPasscodeSalt: 'done.invoke.auth.introSlider:invocation[0]'; @@ -48,7 +49,7 @@ export interface Typegen0 { hasPasscodeSet: ''; }; eventsCausingServices: { - downloadFaceSdkModel: 'LOGIN' | 'SETUP_PASSCODE'; + downloadFaceSdkModel: 'LOGIN' | 'SETUP_BIOMETRICS' | 'SETUP_PASSCODE'; generatePasscodeSalt: 'SELECT'; }; matchesStates: diff --git a/machines/backupAndRestore/backupRestore.ts b/machines/backupAndRestore/backupRestore.ts index 0f4e888b..a0fd2a30 100644 --- a/machines/backupAndRestore/backupRestore.ts +++ b/machines/backupAndRestore/backupRestore.ts @@ -20,7 +20,7 @@ import { sendErrorEvent, getErrorEventData, } from '../../shared/telemetry/TelemetryUtils'; -import {VcEvents} from '../VCItemMachine/vc'; +import {VcEvents} from '../VerifiableCredential/VCMetaMachine/vc'; import {NETWORK_REQUEST_FAILED, TECHNICAL_ERROR} from '../../shared/constants'; import NetInfo, {NetInfoState} from '@react-native-community/netinfo'; diff --git a/machines/bleShare/request/requestMachine.ts b/machines/bleShare/request/requestMachine.ts index f21aacf0..d6542d93 100644 --- a/machines/bleShare/request/requestMachine.ts +++ b/machines/bleShare/request/requestMachine.ts @@ -12,7 +12,7 @@ import {createModel} from 'xstate/lib/model'; import {DeviceInfo} from '../../../components/DeviceInfoList'; import {getDeviceNameSync} from 'react-native-device-info'; import {StoreEvents} from '../../store'; -import {VC} from '../../../types/VC/ExistingMosipVC/vc'; +import {VC} from '../../../types/VC/vc'; import {AppServices} from '../../../shared/GlobalContext'; import { androidVersion, @@ -20,9 +20,8 @@ import { RECEIVED_VCS_STORE_KEY, } from '../../../shared/constants'; import {ActivityLogEvents, ActivityLogType} from '../../activityLog'; -import {VcEvents} from '../../VCItemMachine/vc'; +import {VcEvents} from '../../VerifiableCredential/VCMetaMachine/vc'; import {subscribe} from '../../../shared/openIdBLE/verifierEventHandler'; -import {log} from 'xstate/lib/actions'; import {VerifierDataEvent} from '@mosip/tuvali/src/types/events'; import {BLEError} from '../types'; import Storage from '../../../shared/storage'; @@ -39,7 +38,6 @@ import { } from '../../../shared/telemetry/TelemetryUtils'; import {TelemetryConstants} from '../../../shared/telemetry/TelemetryConstants'; import {getIdType} from '../../../shared/openId4VCI/Utils'; -// import { verifyPresentation } from '../shared/vcjs/verifyPresentation'; const {verifier, EventTypes, VerificationStatus} = tuvali; @@ -103,11 +101,6 @@ export const requestMachine = schema: { context: model.initialContext, events: {} as EventFrom, - services: {} as { - verifyVp: { - data: VC; - }; - }, }, invoke: { src: 'monitorConnection', @@ -324,7 +317,6 @@ export const requestMachine = states: { idle: {}, verifyingIdentity: { - exit: 'clearShouldVerifyPresence', on: { FACE_VALID: { target: 'accepting', @@ -352,22 +344,6 @@ export const requestMachine = }, }, }, - verifyingVp: { - invoke: { - src: 'verifyVp', - onDone: [ - { - target: 'accepting', - }, - ], - onError: [ - { - target: 'idle', - actions: log('Failed to verify Verifiable Presentation'), - }, - ], - }, - }, accepting: { initial: 'prependingReceivedVcMetadata', states: { @@ -537,15 +513,7 @@ export const requestMachine = }), setIncomingVc: assign({ - incomingVc: (_context, event) => { - const vp = event.vc.verifiablePresentation; - return vp != null - ? { - ...event.vc, - verifiableCredential: vp.verifiableCredential[0], - } - : event.vc; - }, + incomingVc: (_context, event) => event.vc, }), setOpenID4VpUri: assign({ @@ -666,13 +634,6 @@ export const requestMachine = to: context => context.serviceRefs.vc, }), - clearShouldVerifyPresence: assign({ - incomingVc: context => ({ - ...context.incomingVc, - shouldVerifyPresence: false, - }), - }), - sendVCReceivingStartEvent: () => { sendStartEvent( getStartEventData(TelemetryConstants.FlowType.receiverVcShare), @@ -887,26 +848,10 @@ export const requestMachine = return () => subscription.remove(); }, - sendVcResponse: (context, _event, meta) => async () => { + sendVcResponse: (_context, _event, meta) => async () => { verifier.sendVerificationStatus(meta.data.status); }, - verifyVp: context => async () => { - const vp = context.incomingVc.verifiablePresentation; - - // TODO - // const challenge = ? - // await verifyPresentation(vp, challenge); - - const vc: VC = { - ...context.incomingVc, - verifiablePresentation: null, - verifiableCredential: vp.verifiableCredential[0], - }; - - return Promise.resolve(vc); - }, - checkStorageAvailability: () => async () => { return Promise.resolve( Storage.isMinimumLimitReached('minStorageRequired'), @@ -915,15 +860,6 @@ export const requestMachine = }, guards: { - hasExistingVc: (context, event) => { - const receivedVcs = event.vcMetadatas; - const incomingVcMetadata = VCMetadata.fromVC(context.incomingVc); - return receivedVcs?.some( - vcMetadata => - vcMetadata.getVcKey() == incomingVcMetadata.getVcKey(), - ); - }, - isMinimumStorageLimitReached: (_context, event) => Boolean(event.data), }, diff --git a/machines/bleShare/request/requestMachine.typegen.ts b/machines/bleShare/request/requestMachine.typegen.ts index e5d7f09c..7f955bd0 100644 --- a/machines/bleShare/request/requestMachine.typegen.ts +++ b/machines/bleShare/request/requestMachine.typegen.ts @@ -9,11 +9,6 @@ export interface Typegen0 { data: unknown; __tip: 'See the XState TS docs to learn how to strongly type this.'; }; - 'done.invoke.request.reviewing.verifyingVp:invocation[0]': { - type: 'done.invoke.request.reviewing.verifyingVp:invocation[0]'; - data: unknown; - __tip: 'See the XState TS docs to learn how to strongly type this.'; - }; 'xstate.after(DESTROY_TIMEOUT)#request.clearingConnection': { type: 'xstate.after(DESTROY_TIMEOUT)#request.clearingConnection'; }; @@ -21,7 +16,6 @@ export interface Typegen0 { type: 'xstate.after(SHARING_TIMEOUT)#request.waitingForVc.inProgress'; }; 'xstate.init': {type: 'xstate.init'}; - 'xstate.stop': {type: 'xstate.stop'}; }; invokeSrcNameMap: { advertiseDevice: 'done.invoke.request.waitingForConnection:invocation[0]'; @@ -40,7 +34,6 @@ export interface Typegen0 { | 'done.invoke.request.reviewing.accepted:invocation[0]' | 'done.invoke.request.reviewing.rejected:invocation[0]' | 'done.invoke.request.reviewing.savingFailed:invocation[0]'; - verifyVp: 'done.invoke.request.reviewing.verifyingVp:invocation[0]'; }; missingImplementations: { actions: never; @@ -49,31 +42,20 @@ export interface Typegen0 { services: never; }; eventsCausingActions: { - clearShouldVerifyPresence: - | 'ACCEPT' - | 'BLE_ERROR' - | 'CANCEL' - | 'FACE_INVALID' - | 'FACE_VALID' - | 'REJECT' - | 'RESET' - | 'SCREEN_BLUR' - | 'SCREEN_FOCUS' - | 'xstate.stop'; logReceived: 'CANCEL' | 'REJECT' | 'STORE_ERROR' | 'STORE_RESPONSE'; openAppPermission: 'GOTO_SETTINGS'; prependReceivedVcMetadata: | 'ACCEPT' | 'DISMISS' | 'FACE_VALID' - | 'VC_RECEIVED' - | 'done.invoke.request.reviewing.verifyingVp:invocation[0]'; + | 'VC_RECEIVED'; registerLoggers: | 'DISCONNECT' | 'RESET' | 'xstate.after(DESTROY_TIMEOUT)#request.clearingConnection'; removeLoggers: | 'DISCONNECT' + | 'GOTO_HOME' | 'RESET' | 'SCREEN_BLUR' | 'xstate.after(DESTROY_TIMEOUT)#request.clearingConnection' @@ -118,13 +100,12 @@ export interface Typegen0 { | 'RESET' | 'done.invoke.request.checkStorage:invocation[0]'; checkStorageAvailability: 'SCREEN_FOCUS'; - disconnect: '' | 'DISMISS' | 'GO_TO_RECEIVED_VC_TAB'; - monitorConnection: 'xstate.init'; + disconnect: '' | 'DISMISS' | 'GOTO_HOME' | 'GO_TO_RECEIVED_VC_TAB'; + monitorConnection: 'GOTO_HOME' | 'xstate.init'; receiveVc: 'CONNECTED'; requestBluetooth: 'BLUETOOTH_STATE_DISABLED'; requestNearByDevicesPermission: 'NEARBY_DISABLED'; sendVcResponse: 'CANCEL' | 'REJECT' | 'STORE_ERROR' | 'STORE_RESPONSE'; - verifyVp: never; }; matchesStates: | 'bluetoothDenied' @@ -157,7 +138,6 @@ export interface Typegen0 { | 'reviewing.savingFailed' | 'reviewing.savingFailed.idle' | 'reviewing.verifyingIdentity' - | 'reviewing.verifyingVp' | 'storageLimitReached' | 'waitingForConnection' | 'waitingForVc' @@ -178,7 +158,6 @@ export interface Typegen0 { | 'rejected' | 'savingFailed' | 'verifyingIdentity' - | 'verifyingVp' | { accepting?: 'prependingReceivedVcMetadata' | 'storingVc'; savingFailed?: 'idle'; diff --git a/machines/bleShare/request/selectors.ts b/machines/bleShare/request/selectors.ts index 7d65ba22..7b670ac1 100644 --- a/machines/bleShare/request/selectors.ts +++ b/machines/bleShare/request/selectors.ts @@ -1,5 +1,7 @@ import {StateFrom} from 'xstate'; import {requestMachine} from './requestMachine'; +import {VCMetadata} from '../../../shared/VCMetadata'; +import {getMosipLogo} from '../../../components/VC/common/VCUtils'; type State = StateFrom; @@ -7,16 +9,31 @@ export function selectSenderInfo(state: State) { return state.context.senderInfo; } -export function selectIncomingVc(state: State) { - return state.context.incomingVc; +export function selectCredential(state: State) { + return new VCMetadata(state.context.incomingVc?.vcMetadata).isFromOpenId4VCI() + ? state.context.incomingVc?.verifiableCredential?.credential + : state.context.incomingVc?.verifiableCredential; } -export function selectSharingProtocol(state: State) { - return state.context.sharingProtocol; -} - -export function selectIsIncomingVp(state: State) { - return state.context.incomingVc?.verifiablePresentation != null; +export function selectVerifiableCredentialData(state: State) { + const vcMetadata = new VCMetadata(state.context.incomingVc?.vcMetadata); + return vcMetadata.isFromOpenId4VCI() + ? { + vcMetadata: vcMetadata, + face: state.context.incomingVc?.verifiableCredential.credential + .credentialSubject.face, + issuerLogo: state.context.incomingVc?.verifiableCredential?.issuerLogo, + wellKnown: state.context.incomingVc?.verifiableCredential?.wellKnown, + credentialTypes: + state.context.incomingVc?.verifiableCredential?.credentialTypes, + issuer: vcMetadata.issuer, + } + : { + vcMetadata: vcMetadata, + issuer: vcMetadata.issuer, + face: state.context.incomingVc?.credential?.biometrics?.face, + issuerLogo: getMosipLogo(), + }; } export function selectIsReviewingInIdle(state: State) { diff --git a/machines/bleShare/scan/scanMachine.ts b/machines/bleShare/scan/scanMachine.ts index 7d0be37e..f3d2a841 100644 --- a/machines/bleShare/scan/scanMachine.ts +++ b/machines/bleShare/scan/scanMachine.ts @@ -14,15 +14,15 @@ import {createModel} from 'xstate/lib/model'; import {EmitterSubscription, Linking} from 'react-native'; import {DeviceInfo} from '../../../components/DeviceInfoList'; import {getDeviceNameSync} from 'react-native-device-info'; -import {VC, VerifiablePresentation} from '../../../types/VC/ExistingMosipVC/vc'; +import {VC} from '../../../types/VC/vc'; import {AppServices} from '../../../shared/GlobalContext'; import {ActivityLogEvents, ActivityLogType} from '../../activityLog'; import { androidVersion, + FACE_AUTH_CONSENT, isAndroid, isIOS, MY_LOGIN_STORE_KEY, - FACE_AUTH_CONSENT, } from '../../../shared/constants'; import {subscribe} from '../../../shared/openIdBLE/walletEventHandler'; import { @@ -39,7 +39,6 @@ import { requestLocationPermission, } from '../../../shared/location'; import {CameraCapturedPicture} from 'expo-camera'; -import {log} from 'xstate/lib/actions'; import {createQrLoginMachine, qrLoginMachine} from '../../QrLoginMachine'; import {StoreEvents} from '../../store'; import {WalletDataEvent} from '@mosip/tuvali/lib/typescript/types/events'; @@ -70,7 +69,6 @@ const model = createModel( receiverInfo: {} as DeviceInfo, selectedVc: {} as VC, bleError: {} as BLEError, - createdVp: null as VC, loggers: [] as EmitterSubscription[], vcName: '', flowType: VCShareFlowType.SIMPLE_SHARE, @@ -122,8 +120,6 @@ const model = createModel( FACE_VALID: () => ({}), FACE_INVALID: () => ({}), RETRY_VERIFICATION: () => ({}), - VP_CREATED: (vp: VerifiablePresentation) => ({vp}), - TOGGLE_USER_CONSENT: () => ({}), RESET: () => ({}), FACE_VERIFICATION_CONSENT: (isConsentGiven: boolean) => ({ isConsentGiven, @@ -144,11 +140,6 @@ export const scanMachine = schema: { context: model.initialContext, events: {} as EventFrom, - services: {} as { - createVp: { - data: VC; - }; - }, }, invoke: { src: 'monitorConnection', @@ -160,7 +151,7 @@ export const scanMachine = target: '#scan.disconnectDevice', }, SCREEN_FOCUS: { - target: 'checkStorage', + target: '.checkStorage', }, BLE_ERROR: { target: '.handlingBleError', @@ -206,6 +197,7 @@ export const scanMachine = }, }, restrictSharingVc: {}, + startPermissionCheck: { on: { START_PERMISSION_CHECK: [ @@ -516,7 +508,7 @@ export const scanMachine = }, reviewing: { initial: 'idle', - entry: ['resetShouldVerifyPresence', send('CHECK_FLOW_TYPE')], + entry: [send('CHECK_FLOW_TYPE')], on: { CHECK_FLOW_TYPE: [ { @@ -534,7 +526,6 @@ export const scanMachine = }, ], }, - exit: ['clearCreatedVp'], states: { idle: {}, selectingVc: { @@ -565,9 +556,6 @@ export const scanMachine = target: 'cancelling', actions: 'sendVCShareFlowCancelEndEvent', }, - TOGGLE_USER_CONSENT: { - actions: 'toggleShouldVerifyPresence', - }, }, }, cancelling: { @@ -707,23 +695,7 @@ export const scanMachine = ], }, }, - creatingVp: { - invoke: { - src: 'createVp', - onDone: [ - { - target: 'sendingVc', - actions: 'setCreatedVp', - }, - ], - onError: [ - { - target: 'selectingVc', - actions: log('Could not create Verifiable Presentation'), - }, - ], - }, - }, + invalidIdentity: { on: { DISMISS: [ @@ -915,12 +887,7 @@ export const scanMachine = }), setSelectedVc: assign({ - selectedVc: (context, event) => { - return { - ...event.vc, - shouldVerifyPresence: context.selectedVc.shouldVerifyPresence, - }; - }, + selectedVc: (_context, event) => event.vc, }), resetSelectedVc: assign({ @@ -935,14 +902,6 @@ export const scanMachine = flowType: VCShareFlowType.SIMPLE_SHARE, }), - setCreatedVp: assign({ - createdVp: (_context, event) => event.data, - }), - - clearCreatedVp: assign({ - createdVp: () => null, - }), - registerLoggers: assign({ loggers: () => { if (__DEV__) { @@ -989,9 +948,9 @@ export const scanMachine = const vcMetadata = context.selectedVc?.vcMetadata; return ActivityLogEvents.LOG_ACTIVITY({ _vcKey: VCMetadata.fromVC(vcMetadata).getVcKey(), - type: context.selectedVc.shouldVerifyPresence - ? 'VC_SHARED_WITH_VERIFICATION_CONSENT' - : context.shareLogType, + type: context.shareLogType + ? context.shareLogType + : 'VC_SHARED_WITH_VERIFICATION_CONSENT', id: vcMetadata.id, idType: getIdType(vcMetadata.issuer), timestamp: Date.now(), @@ -1018,25 +977,11 @@ export const scanMachine = {to: context => context.serviceRefs.activityLog}, ), - toggleShouldVerifyPresence: assign({ - selectedVc: context => ({ - ...context.selectedVc, - shouldVerifyPresence: !context.selectedVc.shouldVerifyPresence, - }), - }), - setLinkCode: assign({ linkCode: (_, event) => new URL(event.params).searchParams.get('linkCode'), }), - resetShouldVerifyPresence: assign({ - selectedVc: context => ({ - ...context.selectedVc, - shouldVerifyPresence: false, - }), - }), - storeLoginItem: send( (_context, event) => { return StoreEvents.PREPEND( @@ -1264,11 +1209,6 @@ export const scanMachine = }, sendVc: context => callback => { - const vp = context.createdVp; - const vc = { - ...(vp != null ? vp : context.selectedVc), - }; - const statusCallback = (event: WalletDataEvent) => { if (event.type === EventTypes.onDataSent) { callback({type: 'VC_SENT'}); @@ -1283,7 +1223,7 @@ export const scanMachine = }; wallet.sendData( JSON.stringify({ - ...vc, + ...context.selectedVc, }), ); const subscription = subscribe(statusCallback); @@ -1298,25 +1238,6 @@ export const scanMachine = } }, - createVp: context => async () => { - // const verifiablePresentation = await createVerifiablePresentation(...); - - const verifiablePresentation: VerifiablePresentation = { - '@context': [''], - proof: null, - type: 'VerifiablePresentation', - verifiableCredential: [context.selectedVc.verifiableCredential], - }; - - const vc: VC = { - ...context.selectedVc, - verifiableCredential: null, - verifiablePresentation, - }; - - return Promise.resolve(vc); - }, - checkStorageAvailability: () => async () => { return Promise.resolve( Storage.isMinimumLimitReached('minStorageRequiredForAuditEntry'), diff --git a/machines/bleShare/scan/scanMachine.typegen.ts b/machines/bleShare/scan/scanMachine.typegen.ts index a335c5df..bc2d4d1b 100644 --- a/machines/bleShare/scan/scanMachine.typegen.ts +++ b/machines/bleShare/scan/scanMachine.typegen.ts @@ -14,16 +14,10 @@ export interface Typegen0 { data: unknown; __tip: 'See the XState TS docs to learn how to strongly type this.'; }; - 'done.invoke.scan.reviewing.creatingVp:invocation[0]': { - type: 'done.invoke.scan.reviewing.creatingVp:invocation[0]'; - data: unknown; - __tip: 'See the XState TS docs to learn how to strongly type this.'; - }; 'xstate.after(DESTROY_TIMEOUT)#scan.clearingConnection': { type: 'xstate.after(DESTROY_TIMEOUT)#scan.clearingConnection'; }; 'xstate.init': {type: 'xstate.init'}; - 'xstate.stop': {type: 'xstate.stop'}; }; invokeSrcNameMap: { checkBluetoothPermission: 'done.invoke.scan.checkBluetoothPermission.checking:invocation[0]'; @@ -34,7 +28,6 @@ export interface Typegen0 { checkLocationStatus: 'done.invoke.scan.checkingLocationState.checkLocationService:invocation[0]'; checkNearByDevicesPermission: 'done.invoke.scan.checkNearbyDevicesPermission.checking:invocation[0]'; checkStorageAvailability: 'done.invoke.scan.checkStorage:invocation[0]'; - createVp: 'done.invoke.scan.reviewing.creatingVp:invocation[0]'; disconnect: | 'done.invoke.scan.clearingConnection:invocation[0]' | 'done.invoke.scan.disconnectDevice:invocation[0]' @@ -53,16 +46,6 @@ export interface Typegen0 { services: never; }; eventsCausingActions: { - clearCreatedVp: - | '' - | 'BLE_ERROR' - | 'DISCONNECT' - | 'RESET' - | 'RETRY' - | 'SCREEN_BLUR' - | 'SCREEN_FOCUS' - | 'SELECT_VC' - | 'xstate.stop'; clearUri: 'STORE_RESPONSE'; getFaceAuthConsent: | 'DISCONNECT' @@ -77,13 +60,11 @@ export interface Typegen0 { | 'DISCONNECT' | 'DISMISS' | 'SCREEN_BLUR' - | 'SCREEN_FOCUS' | 'STORE_RESPONSE' | 'xstate.init'; resetFaceCaptureBannerStatus: 'ACCEPT_REQUEST' | 'CLOSE_BANNER'; - resetFlowType: 'DISMISS'; - resetSelectedVc: 'DISMISS'; - resetShouldVerifyPresence: 'CANCEL' | 'CONNECTED' | 'DISMISS' | 'RETRY'; + resetFlowType: 'GOTO_HISTORY'; + resetSelectedVc: 'GOTO_HISTORY'; sendBLEConnectionErrorEvent: 'BLE_ERROR'; sendScanData: 'SCAN'; sendVCShareFlowCancelEndEvent: 'CANCEL'; @@ -92,7 +73,6 @@ export interface Typegen0 { sendVcSharingStartEvent: 'SCAN'; setBleError: 'BLE_ERROR'; setChildRef: 'STORE_RESPONSE'; - setCreatedVp: 'done.invoke.scan.reviewing.creatingVp:invocation[0]'; setFlowType: 'SELECT_VC'; setLinkCode: 'SCAN'; setReadyForBluetoothStateCheck: 'BLUETOOTH_PERMISSION_ENABLED'; @@ -106,18 +86,13 @@ export interface Typegen0 { storeLoginItem: 'done.invoke.QrLogin'; storeShowFaceAuthConsent: 'FACE_VERIFICATION_CONSENT'; storingActivityLog: 'STORE_RESPONSE'; - toggleShouldVerifyPresence: 'TOGGLE_USER_CONSENT'; updateFaceCaptureBannerStatus: 'FACE_VALID'; updateShowFaceAuthConsent: 'STORE_RESPONSE'; }; eventsCausingDelays: { CONNECTION_TIMEOUT: 'SCAN'; DESTROY_TIMEOUT: '' | 'DISMISS' | 'LOCATION_ENABLED' | 'RETRY'; - SHARING_TIMEOUT: - | 'ACCEPT_REQUEST' - | 'CHECK_FLOW_TYPE' - | 'FACE_VALID' - | 'done.invoke.scan.reviewing.creatingVp:invocation[0]'; + SHARING_TIMEOUT: 'ACCEPT_REQUEST' | 'CHECK_FLOW_TYPE' | 'FACE_VALID'; }; eventsCausingGuards: { isFlowTypeMiniViewShare: 'CHECK_FLOW_TYPE'; @@ -142,21 +117,12 @@ export interface Typegen0 { checkLocationStatus: '' | 'LOCATION_REQUEST'; checkNearByDevicesPermission: 'APP_ACTIVE' | 'START_PERMISSION_CHECK'; checkStorageAvailability: 'RESET' | 'SCREEN_FOCUS' | 'SELECT_VC'; - createVp: never; disconnect: '' | 'DISMISS' | 'LOCATION_ENABLED' | 'RETRY' | 'SCREEN_BLUR'; - monitorConnection: - | 'DISMISS' - | 'SCREEN_BLUR' - | 'SCREEN_FOCUS' - | 'xstate.init'; + monitorConnection: 'DISMISS' | 'SCREEN_BLUR' | 'xstate.init'; requestBluetooth: 'BLUETOOTH_STATE_DISABLED'; requestNearByDevicesPermission: 'NEARBY_DISABLED'; requestToEnableLocationPermission: 'LOCATION_DISABLED'; - sendVc: - | 'ACCEPT_REQUEST' - | 'CHECK_FLOW_TYPE' - | 'FACE_VALID' - | 'done.invoke.scan.reviewing.creatingVp:invocation[0]'; + sendVc: 'ACCEPT_REQUEST' | 'CHECK_FLOW_TYPE' | 'FACE_VALID'; startConnection: 'SCAN'; }; matchesStates: @@ -199,7 +165,6 @@ export interface Typegen0 { | 'reviewing' | 'reviewing.accepted' | 'reviewing.cancelling' - | 'reviewing.creatingVp' | 'reviewing.disconnect' | 'reviewing.faceVerificationConsent' | 'reviewing.idle' @@ -232,7 +197,6 @@ export interface Typegen0 { reviewing?: | 'accepted' | 'cancelling' - | 'creatingVp' | 'disconnect' | 'faceVerificationConsent' | 'idle' diff --git a/machines/bleShare/scan/selectors.ts b/machines/bleShare/scan/selectors.ts index c9520da2..fd941ce6 100644 --- a/machines/bleShare/scan/selectors.ts +++ b/machines/bleShare/scan/selectors.ts @@ -1,5 +1,7 @@ import {StateFrom} from 'xstate'; import {scanMachine} from './scanMachine'; +import {VCMetadata} from '../../../shared/VCMetadata'; +import {getMosipLogo} from '../../../components/VC/common/VCUtils'; type State = StateFrom; @@ -15,8 +17,31 @@ export function selectVcName(state: State) { return state.context.vcName; } -export function selectSelectedVc(state: State) { - return state.context.selectedVc; +export function selectCredential(state: State) { + return new VCMetadata(state.context.selectedVc?.vcMetadata).isFromOpenId4VCI() + ? state.context.selectedVc?.verifiableCredential?.credential + : state.context.selectedVc?.verifiableCredential; +} + +export function selectVerifiableCredentialData(state: State) { + const vcMetadata = new VCMetadata(state.context.selectedVc?.vcMetadata); + return vcMetadata.isFromOpenId4VCI() + ? { + vcMetadata: vcMetadata, + issuer: vcMetadata.issuer, + issuerLogo: state.context.selectedVc?.verifiableCredential?.issuerLogo, + wellKnown: state.context.selectedVc?.verifiableCredential?.wellKnown, + face: state.context.selectedVc?.verifiableCredential?.credential + .credentialSubject?.face, + credentialTypes: + state.context.selectedVc?.verifiableCredential?.credentialTypes, + } + : { + vcMetadata: vcMetadata, + issuer: vcMetadata.issuer, + face: state.context.selectedVc?.credential?.biometrics?.face, + issuerLogo: getMosipLogo(), + }; } export function selectQrLoginRef(state: State) { diff --git a/machines/issuersMachine.ts b/machines/issuersMachine.ts index e1211fdf..5e116fe6 100644 --- a/machines/issuersMachine.ts +++ b/machines/issuersMachine.ts @@ -39,10 +39,7 @@ import { } from '../shared/telemetry/TelemetryUtils'; import {TelemetryConstants} from '../shared/telemetry/TelemetryConstants'; -import { - CredentialWrapper, - VerifiableCredential, -} from '../types/VC/EsignetMosipVC/vc'; +import {CredentialWrapper, VerifiableCredential} from '../types/VC/vc'; import {CACHED_API} from '../shared/api'; import {request} from '../shared/request'; import {BiometricCancellationError} from '../shared/error/BiometricCancellationError'; @@ -532,7 +529,7 @@ export const IssuersMachine = model.createMachine( storeVcsContext: send( context => { return { - type: 'VC_DOWNLOADED_FROM_OPENID4VCI', + type: 'VC_DOWNLOADED', vcMetadata: getVCMetadata(context), vc: context.credentialWrapper, }; diff --git a/machines/issuersMachine.typegen.ts b/machines/issuersMachine.typegen.ts index 85215deb..6a478cad 100644 --- a/machines/issuersMachine.typegen.ts +++ b/machines/issuersMachine.typegen.ts @@ -125,6 +125,7 @@ export interface Typegen0 { | 'SELECTED_ISSUER' | 'TRY_AGAIN' | 'done.invoke.issuersMachine.performAuthorization:invocation[0]'; + setMetadataInCredentialData: 'done.invoke.issuersMachine.verifyingCredential:invocation[0]'; setNoInternet: 'done.invoke.checkInternet'; setOIDCConfigError: 'error.platform.issuersMachine.performAuthorization:invocation[0]'; setPrivateKey: 'done.invoke.issuersMachine.generateKeyPair:invocation[0]'; @@ -132,6 +133,7 @@ export interface Typegen0 { setSelectedIssuerId: 'SELECTED_ISSUER'; setSelectedIssuers: 'done.invoke.issuersMachine.downloadIssuerConfig:invocation[0]'; setTokenResponse: 'done.invoke.issuersMachine.performAuthorization:invocation[0]'; + setVCMetadata: 'done.invoke.issuersMachine.verifyingCredential:invocation[0]'; setVerifiableCredential: 'done.invoke.issuersMachine.downloadCredentials:invocation[0]'; storeKeyPair: 'done.invoke.issuersMachine.generateKeyPair:invocation[0]'; storeVcMetaContext: 'done.invoke.issuersMachine.verifyingCredential:invocation[0]'; diff --git a/machines/settings.ts b/machines/settings.ts index e79ec2bb..77915e0a 100644 --- a/machines/settings.ts +++ b/machines/settings.ts @@ -4,15 +4,15 @@ import {AppServices} from '../shared/GlobalContext'; import { APP_ID_DICTIONARY, APP_ID_LENGTH, - MIMOTO_BASE_URL, - isIOS, - SETTINGS_STORE_KEY, + COMMON_PROPS_KEY, ESIGNET_BASE_URL, + isIOS, + MIMOTO_BASE_URL, + SETTINGS_STORE_KEY, } from '../shared/constants'; -import {VCLabel} from '../types/VC/ExistingMosipVC/vc'; +import {VCLabel} from '../types/VC/vc'; import {StoreEvents} from './store'; import getAllConfigurations from '../shared/api'; -import {COMMON_PROPS_KEY} from '../shared/constants'; import Storage from '../shared/storage'; import ShortUniqueId from 'short-unique-id'; import {__AppId} from '../shared/GlobalVariables'; diff --git a/machines/settings.typegen.ts b/machines/settings.typegen.ts index 5e19884f..37a729a1 100644 --- a/machines/settings.typegen.ts +++ b/machines/settings.typegen.ts @@ -26,8 +26,10 @@ export interface Typegen0 { eventsCausingActions: { requestStoredContext: 'xstate.init'; resetCredentialRegistryResponse: 'CANCEL' | 'UPDATE_HOST'; + resetIsBiometricToggled: 'DISMISS'; setBackupAndRestoreOptionExplored: 'SET_IS_BACKUP_AND_RESTORE_EXPLORED'; setContext: 'STORE_RESPONSE'; + setIsBiometricToggled: 'TOGGLE_BIOMETRIC_UNLOCK'; storeContext: | 'ACCEPT_HARDWARE_SUPPORT_NOT_EXISTS' | 'SET_IS_BACKUP_AND_RESTORE_EXPLORED' diff --git a/screens/Home/HomeScreen.tsx b/screens/Home/HomeScreen.tsx index 3b19332a..8ead984e 100644 --- a/screens/Home/HomeScreen.tsx +++ b/screens/Home/HomeScreen.tsx @@ -10,13 +10,13 @@ import {useHomeScreen} from './HomeScreenController'; import {TabRef} from './HomeScreenMachine'; import {useTranslation} from 'react-i18next'; import {ActorRefFrom} from 'xstate'; -import {ExistingMosipVCItemMachine} from '../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine'; import LinearGradient from 'react-native-linear-gradient'; -import {EsignetMosipVCItemMachine} from '../../machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine'; import {ErrorMessageOverlay} from '../../components/MessageOverlay'; import {Pressable} from 'react-native'; import testIDProps from '../../shared/commonUtil'; import {BannerNotificationContainer} from '../../components/BannerNotificationContainer'; +import {VCItemMachine} from '../../machines/VerifiableCredential/VCItemMachine/VCItemMachine'; +import {VerifiableCredential} from '../../types/VC/vc'; export const HomeScreen: React.FC = props => { const controller = useHomeScreen(props); @@ -107,7 +107,6 @@ export const HomeScreen: React.FC = props => { export interface HomeScreenTabProps { isVisible: boolean; service: TabRef; - vcItemActor: - | ActorRefFrom - | ActorRefFrom; + vcItemActor: ActorRefFrom; + vc: VerifiableCredential | Credential; } diff --git a/screens/Home/HomeScreenController.ts b/screens/Home/HomeScreenController.ts index 9e9b820b..51c2b5b3 100644 --- a/screens/Home/HomeScreenController.ts +++ b/screens/Home/HomeScreenController.ts @@ -13,6 +13,7 @@ import { selectIssuersMachine, selectIsMinimumStorageLimitReached, } from './HomeScreenMachine'; +import {selectVc} from '../../machines/VerifiableCredential/VCItemMachine/VCItemSelectors'; let homeMachineService; function useCreateHomeMachineService() { @@ -31,8 +32,6 @@ export function getHomeMachineService() { } export function useHomeScreen(props: HomeRouteProps) { - const {appService} = useContext(GlobalContext); - const vcService = appService.children.get('vc'); const service = useCreateHomeMachineService(); useEffect(() => { @@ -43,20 +42,17 @@ export function useHomeScreen(props: HomeRouteProps) { return { service, - activeTab: useSelector(service, selectActiveTab), selectedVc: useSelector(service, selectSelectedVc), + vc: useSelector(service, selectVc), tabRefs: useSelector(service, selectTabRefs), - isViewingVc: useSelector(service, selectViewingVc), haveTabsLoaded: useSelector(service, selectTabsLoaded), - IssuersService: useSelector(service, selectIssuersMachine), isMinimumStorageLimitReached: useSelector( service, selectIsMinimumStorageLimitReached, ), - DISMISS: () => service.send(HomeScreenEvents.DISMISS()), GOTO_ISSUERS: () => service.send(HomeScreenEvents.GOTO_ISSUERS()), DISMISS_MODAL: () => service.send(HomeScreenEvents.DISMISS_MODAL()), diff --git a/screens/Home/HomeScreenMachine.ts b/screens/Home/HomeScreenMachine.ts index f0dda549..57fd6c1d 100644 --- a/screens/Home/HomeScreenMachine.ts +++ b/screens/Home/HomeScreenMachine.ts @@ -1,15 +1,14 @@ import {ActorRefFrom, assign, EventFrom, send, spawn, StateFrom} from 'xstate'; import {createModel} from 'xstate/lib/model'; -import {ExistingMosipVCItemMachine} from '../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine'; import {AppServices} from '../../shared/GlobalContext'; import {createMyVcsTabMachine, MyVcsTabMachine} from './MyVcsTabMachine'; import { createReceivedVcsTabMachine, ReceivedVcsTabMachine, } from './ReceivedVcsTabMachine'; -import {EsignetMosipVCItemMachine} from '../../machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine'; import {IssuersMachine} from '../../machines/issuersMachine'; import Storage from '../../shared/storage'; +import {VCItemMachine} from '../../machines/VerifiableCredential/VCItemMachine/VCItemMachine'; const model = createModel( { @@ -18,9 +17,7 @@ const model = createModel( myVcs: {} as ActorRefFrom, receivedVcs: {} as ActorRefFrom, }, - selectedVc: null as - | ActorRefFrom - | ActorRefFrom, + selectedVc: ActorRefFrom, activeTab: 0, }, { @@ -28,11 +25,7 @@ const model = createModel( SELECT_MY_VCS: () => ({}), SELECT_RECEIVED_VCS: () => ({}), SELECT_HISTORY: () => ({}), - VIEW_VC: ( - vcItemActor: - | ActorRefFrom - | ActorRefFrom, - ) => ({ + VIEW_VC: (vcItemActor: ActorRefFrom) => ({ vcItemActor, }), DISMISS_MODAL: () => ({}), diff --git a/screens/Home/MyVcs/AddVcModalMachine.ts b/screens/Home/MyVcs/AddVcModalMachine.ts index 2634cac9..e4ffe1fc 100644 --- a/screens/Home/MyVcs/AddVcModalMachine.ts +++ b/screens/Home/MyVcs/AddVcModalMachine.ts @@ -9,7 +9,7 @@ import { } from 'xstate'; import {createModel} from 'xstate/lib/model'; import {BackendResponseError, request} from '../../../shared/request'; -import {VcIdType} from '../../../types/VC/ExistingMosipVC/vc'; +import {VcIdType} from '../../../types/VC/vc'; import i18n from '../../../i18n'; import {VCMetadata} from '../../../shared/VCMetadata'; import { diff --git a/screens/Home/MyVcs/HistoryTab.tsx b/screens/Home/MyVcs/HistoryTab.tsx index b11be6ce..fd435127 100644 --- a/screens/Home/MyVcs/HistoryTab.tsx +++ b/screens/Home/MyVcs/HistoryTab.tsx @@ -7,7 +7,7 @@ import {ActivityLogText} from '../../../components/ActivityLogText'; import {useKebabPopUp} from '../../../components/KebabPopUpController'; import {VCMetadata} from '../../../shared/VCMetadata'; import {ActorRefFrom} from 'xstate'; -import {ExistingMosipVCItemMachine} from '../../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine'; +import {VCItemMachine} from '../../../machines/VerifiableCredential/VCItemMachine/VCItemMachine'; export const HistoryTab: React.FC = props => { const {t} = useTranslation('HistoryTab'); @@ -57,6 +57,6 @@ export const HistoryTab: React.FC = props => { }; export interface HistoryTabProps { - service: ActorRefFrom; + service: ActorRefFrom; vcMetadata: VCMetadata; } diff --git a/screens/Home/MyVcs/IdInputModalController.ts b/screens/Home/MyVcs/IdInputModalController.ts index 229539bb..5f6fb993 100644 --- a/screens/Home/MyVcs/IdInputModalController.ts +++ b/screens/Home/MyVcs/IdInputModalController.ts @@ -5,16 +5,16 @@ import {ModalProps} from '../../../components/ui/Modal'; import { AddVcModalEvents, AddVcModalMachine, - selectIsAcceptingOtpInput, - selectIsInvalid, - selectIsRequestingOtp, - selectOtpError, selectId, selectIdError, selectIdInputRef, selectIdType, + selectIsAcceptingOtpInput, + selectIsInvalid, + selectIsRequestingOtp, + selectOtpError, } from './AddVcModalMachine'; -import {VcIdType} from '../../../types/VC/ExistingMosipVC/vc'; +import {VcIdType} from '../../../types/VC/vc'; import {IndividualId} from '../../../shared/constants'; export function useIdInputModal({service}: IdInputModalProps) { diff --git a/screens/Home/MyVcs/OtpVerificationModalController.ts b/screens/Home/MyVcs/OtpVerificationModalController.ts index 79999dcc..746d5139 100644 --- a/screens/Home/MyVcs/OtpVerificationModalController.ts +++ b/screens/Home/MyVcs/OtpVerificationModalController.ts @@ -6,6 +6,7 @@ import { import {ActorRefFrom} from 'xstate'; import {ModalProps} from '../../../components/ui/Modal'; import {useSelector} from '@xstate/react'; +import {VCItemMachine} from '../../../machines/VerifiableCredential/VCItemMachine/VCItemMachine'; export function useOtpVerificationModal({service}: OtpVerificationModalProps) { return { @@ -18,7 +19,7 @@ export function useOtpVerificationModal({service}: OtpVerificationModalProps) { } export interface OtpVerificationModalProps extends ModalProps { - service: ActorRefFrom; + service: ActorRefFrom; onInputDone: (otp: string) => void; error?: string; resend?: () => void; diff --git a/screens/Home/MyVcs/RemoveVcWarningOverlay.tsx b/screens/Home/MyVcs/RemoveVcWarningOverlay.tsx index c28d2733..401a33d8 100644 --- a/screens/Home/MyVcs/RemoveVcWarningOverlay.tsx +++ b/screens/Home/MyVcs/RemoveVcWarningOverlay.tsx @@ -8,7 +8,7 @@ import {SvgImage} from '../../../components/ui/svg'; import {useKebabPopUp} from '../../../components/KebabPopUpController'; import {VCMetadata} from '../../../shared/VCMetadata'; import {ActorRefFrom} from 'xstate'; -import {ExistingMosipVCItemEvents} from '../../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine'; +import {VCItemEvents} from '../../../machines/VerifiableCredential/VCItemMachine/VCItemMachine'; export const RemoveVcWarningOverlay: React.FC< RemoveVcWarningOverlayProps @@ -80,6 +80,6 @@ export const RemoveVcWarningOverlay: React.FC< interface RemoveVcWarningOverlayProps { testID: string; - service: ActorRefFrom; + service: ActorRefFrom; vcMetadata: VCMetadata; } diff --git a/screens/Home/MyVcs/WalletBinding.tsx b/screens/Home/MyVcs/WalletBinding.tsx index d60fd49c..c86c3237 100644 --- a/screens/Home/MyVcs/WalletBinding.tsx +++ b/screens/Home/MyVcs/WalletBinding.tsx @@ -1,4 +1,4 @@ -import React, {useEffect} from 'react'; +import React from 'react'; import {Icon} from 'react-native-elements'; import {Theme} from '../../../components/ui/styleUtils'; import {useTranslation} from 'react-i18next'; @@ -7,15 +7,9 @@ import {OtpVerificationModal} from './OtpVerificationModal'; import {MessageOverlay} from '../../../components/MessageOverlay'; import {useKebabPopUp} from '../../../components/KebabPopUpController'; import {ActorRefFrom} from 'xstate'; -import {ExistingMosipVCItemMachine} from '../../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine'; import {VCMetadata} from '../../../shared/VCMetadata'; -import { - getEndEventData, - getErrorEventData, - sendEndEvent, - sendErrorEvent, -} from '../../../shared/telemetry/TelemetryUtils'; import {TelemetryConstants} from '../../../shared/telemetry/TelemetryConstants'; +import {VCItemMachine} from '../../../machines/VerifiableCredential/VCItemMachine/VCItemMachine'; export const WalletVerified: React.FC = () => { return ( @@ -30,29 +24,6 @@ export const WalletVerified: React.FC = () => { export const WalletBinding: React.FC = props => { const controller = useKebabPopUp(props); - - useEffect(() => { - let error = controller.walletBindingError; - if (error) { - error = controller.bindingAuthFailedError - ? controller.bindingAuthFailedError + '-' + error - : error; - sendErrorEvent( - getErrorEventData( - TelemetryConstants.FlowType.vcActivation, - TelemetryConstants.ErrorId.activationFailed, - error, - ), - ); - sendEndEvent( - getEndEventData( - TelemetryConstants.FlowType.vcActivation, - TelemetryConstants.EndEventStatus.failure, - ), - ); - } - }, [controller.walletBindingError]); - const {t} = useTranslation('WalletBinding'); return ( <> @@ -70,8 +41,8 @@ export const WalletBinding: React.FC = props => { onInputDone={controller.INPUT_OTP} error={controller.otpError} resend={controller.RESEND_OTP} - phone={controller.phoneNumber} - email={controller.email} + phone={controller.communicationDetails.phoneNumber} + email={controller.communicationDetails.emailId} flow={TelemetryConstants.FlowType.vcActivationFromKebab} /> )} @@ -83,7 +54,7 @@ export const WalletBinding: React.FC = props => { onButtonPress={controller.CANCEL} /> @@ -93,6 +64,6 @@ export const WalletBinding: React.FC = props => { interface WalletBindingProps { testID?: string; - service: ActorRefFrom; + service: ActorRefFrom; vcMetadata: VCMetadata; } diff --git a/screens/Home/MyVcsTab.tsx b/screens/Home/MyVcsTab.tsx index c7a0a6dc..323d3e6c 100644 --- a/screens/Home/MyVcsTab.tsx +++ b/screens/Home/MyVcsTab.tsx @@ -132,9 +132,11 @@ export const MyVcsTab: React.FC = props => { }; useEffect(() => { - if (controller.areAllVcsLoaded) { + const areAllVcsLoaded = + controller.inProgressVcDownloads.size == 0 ? true : false; + if (areAllVcsLoaded) { controller.RESET_STORE_VC_ITEM_STATUS(); - controller.RESET_ARE_ALL_VCS_DOWNLOADED(); + controller.RESET_IN_PROGRESS_VCS_DOWNLOADED(); } if (controller.inProgressVcDownloads?.size > 0) { controller.SET_STORE_VC_ITEM_STATUS(); @@ -159,11 +161,7 @@ export const MyVcsTab: React.FC = props => { ), ); } - }, [ - controller.areAllVcsLoaded, - controller.inProgressVcDownloads, - controller.isTampered, - ]); + }, [controller.inProgressVcDownloads, controller.isTampered]); let failedVCsList = []; controller.downloadFailedVcs.forEach(vc => { diff --git a/screens/Home/MyVcsTabController.ts b/screens/Home/MyVcsTabController.ts index 9eca8974..52ca7c5c 100644 --- a/screens/Home/MyVcsTabController.ts +++ b/screens/Home/MyVcsTabController.ts @@ -2,57 +2,48 @@ import {useSelector} from '@xstate/react'; import {useContext} from 'react'; import {ActorRefFrom} from 'xstate'; import { + selectDownloadingFailedVcs, + selectInProgressVcDownloads, selectIsRefreshingMyVcs, + selectIsTampered, + selectMyVcs, selectMyVcsMetadata, + selectVerificationErrorMessage, selectWalletBindingSuccess, VcEvents, - selectAreAllVcsDownloaded, - selectInProgressVcDownloads, - selectIsTampered, - selectDownloadingFailedVcs, - selectMyVcs, - selectVerificationErrorMessage, -} from '../../machines/VCItemMachine/vc'; +} from '../../machines/VerifiableCredential/VCMetaMachine/vc'; import { selectWalletBindingError, selectShowWalletBindingError, -} from '../../machines/VCItemMachine/commonSelectors'; -import {ExistingMosipVCItemMachine} from '../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine'; +} from '../../machines/VerifiableCredential/VCItemMachine/VCItemSelectors'; import {GlobalContext} from '../../shared/GlobalContext'; import {HomeScreenTabProps} from './HomeScreen'; import { MyVcsTabEvents, MyVcsTabMachine, selectAddVcModal, - selectIsRequestSuccessful, selectGetVcModal, - selectIsSavingFailedInIdle, selectIsNetworkOff, + selectIsRequestSuccessful, + selectIsSavingFailedInIdle, } from './MyVcsTabMachine'; import { selectShowHardwareKeystoreNotExistsAlert, SettingsEvents, } from '../../machines/settings'; -import {EsignetMosipVCItemMachine} from '../../machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine'; -import {NavigationProp, useNavigation} from '@react-navigation/native'; -import {RootRouteProps} from '../../routes'; - -type MyVcsTabNavigation = NavigationProp; +import {VCItemMachine} from '../../machines/VerifiableCredential/VCItemMachine/VCItemMachine'; export function useMyVcsTab(props: HomeScreenTabProps) { const service = props.service as ActorRefFrom; const {appService} = useContext(GlobalContext); - const vcService = appService.children.get('vc'); - const settingsService = appService.children.get('settings'); - const navigation = useNavigation(); + const vcService = appService.children.get('vc')!!; + const settingsService = appService.children.get('settings')!!; return { service, AddVcModalService: useSelector(service, selectAddVcModal), GetVcModalService: useSelector(service, selectGetVcModal), - vcMetadatas: useSelector(vcService, selectMyVcsMetadata), - isRefreshingVcs: useSelector(vcService, selectIsRefreshingMyVcs), isRequestSuccessful: useSelector(service, selectIsRequestSuccessful), isSavingFailedInIdle: useSelector(service, selectIsSavingFailedInIdle), @@ -60,31 +51,27 @@ export function useMyVcsTab(props: HomeScreenTabProps) { isBindingError: useSelector(service, selectShowWalletBindingError), isBindingSuccess: useSelector(vcService, selectWalletBindingSuccess), isNetworkOff: useSelector(service, selectIsNetworkOff), + inProgressVcDownloads: useSelector(vcService, selectInProgressVcDownloads), + isTampered: useSelector(vcService, selectIsTampered), + downloadFailedVcs: useSelector(vcService, selectDownloadingFailedVcs), + vcData: useSelector(vcService, selectMyVcs), showHardwareKeystoreNotExistsAlert: useSelector( settingsService, selectShowHardwareKeystoreNotExistsAlert, ), - areAllVcsLoaded: useSelector(vcService, selectAreAllVcsDownloaded), - inProgressVcDownloads: useSelector(vcService, selectInProgressVcDownloads), - - isTampered: useSelector(vcService, selectIsTampered), - - downloadFailedVcs: useSelector(vcService, selectDownloadingFailedVcs), verificationErrorMessage: useSelector( vcService, selectVerificationErrorMessage, ), - vcData: useSelector(vcService, selectMyVcs), - SET_STORE_VC_ITEM_STATUS: () => service.send(MyVcsTabEvents.SET_STORE_VC_ITEM_STATUS()), RESET_STORE_VC_ITEM_STATUS: () => service.send(MyVcsTabEvents.RESET_STORE_VC_ITEM_STATUS()), - RESET_ARE_ALL_VCS_DOWNLOADED: () => - vcService.send(VcEvents.RESET_ARE_ALL_VCS_DOWNLOADED()), + RESET_IN_PROGRESS_VCS_DOWNLOADED: () => + vcService.send(VcEvents.RESET_IN_PROGRESS_VCS_DOWNLOADED()), DISMISS: () => service.send(MyVcsTabEvents.DISMISS()), @@ -96,11 +83,7 @@ export function useMyVcsTab(props: HomeScreenTabProps) { REFRESH: () => vcService.send(VcEvents.REFRESH_MY_VCS()), - VIEW_VC: ( - vcRef: - | ActorRefFrom - | ActorRefFrom, - ) => { + VIEW_VC: (vcRef: ActorRefFrom) => { return service.send(MyVcsTabEvents.VIEW_VC(vcRef)); }, @@ -111,7 +94,9 @@ export function useMyVcsTab(props: HomeScreenTabProps) { settingsService.send(SettingsEvents.ACCEPT_HARDWARE_SUPPORT_NOT_EXISTS()), REMOVE_TAMPERED_VCS: () => vcService?.send(VcEvents.REMOVE_TAMPERED_VCS()), + DELETE_VC: () => vcService?.send(VcEvents.DELETE_VC()), + RESET_VERIFY_ERROR: () => { vcService?.send(VcEvents.RESET_VERIFY_ERROR()); }, diff --git a/screens/Home/MyVcsTabMachine.ts b/screens/Home/MyVcsTabMachine.ts index 0e4a7c06..900a37fc 100644 --- a/screens/Home/MyVcsTabMachine.ts +++ b/screens/Home/MyVcsTabMachine.ts @@ -9,15 +9,14 @@ import { } from 'xstate'; import {createModel} from 'xstate/lib/model'; import {StoreEvents} from '../../machines/store'; -import {VcEvents} from '../../machines/VCItemMachine/vc'; -import {ExistingMosipVCItemMachine} from '../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine'; +import {VcEvents} from '../../machines/VerifiableCredential/VCMetaMachine/vc'; import {AppServices} from '../../shared/GlobalContext'; import {MY_VCS_STORE_KEY} from '../../shared/constants'; import {AddVcModalMachine} from './MyVcs/AddVcModalMachine'; import {GetVcModalMachine} from './MyVcs/GetVcModalMachine'; import {VCMetadata} from '../../shared/VCMetadata'; -import {EsignetMosipVCItemMachine} from '../../machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine'; import NetInfo from '@react-native-community/netinfo'; +import {VCItemMachine} from '../../machines/VerifiableCredential/VCItemMachine/VCItemMachine'; const model = createModel( { @@ -26,11 +25,7 @@ const model = createModel( }, { events: { - VIEW_VC: ( - vcItemActor: - | ActorRefFrom - | ActorRefFrom, - ) => ({ + VIEW_VC: (vcItemActor: ActorRefFrom) => ({ vcItemActor, }), DISMISS: () => ({}), diff --git a/screens/Home/ReceivedVcsTabController.ts b/screens/Home/ReceivedVcsTabController.ts index 6beeb554..57d8948c 100644 --- a/screens/Home/ReceivedVcsTabController.ts +++ b/screens/Home/ReceivedVcsTabController.ts @@ -1,12 +1,10 @@ -import {useSelector, useInterpret} from '@xstate/react'; +import {useInterpret, useSelector} from '@xstate/react'; import {useContext, useRef, useState} from 'react'; import {ActorRefFrom} from 'xstate'; import { - VcEvents, selectIsRefreshingReceivedVcs, selectReceivedVcsMetadata, -} from '../../machines/VCItemMachine/vc'; -import {ExistingMosipVCItemMachine} from '../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine'; +} from '../../machines/VerifiableCredential/VCMetaMachine/vc'; import {GlobalContext} from '../../shared/GlobalContext'; import { ReceivedVcsTabEvents, @@ -20,6 +18,8 @@ import { selectTabRefs, selectViewingVc, } from './HomeScreenMachine'; +import {VCItemMachine} from '../../machines/VerifiableCredential/VCItemMachine/VCItemMachine'; +import {selectVc} from '../../machines/VerifiableCredential/VCItemMachine/VCItemSelectors'; export function useReceivedVcsTab() { const [isVisible, setIsVisible] = useState(false); @@ -37,6 +37,8 @@ export function useReceivedVcsTab() { const selectedVc = useSelector(service, selectSelectedVc); + const vc = useSelector(service, selectVc); + const isViewingVc = useSelector(service, selectViewingVc); const ReceivedVcsService = tabRefs.receivedVcs as ActorRefFrom< @@ -54,11 +56,12 @@ export function useReceivedVcsTab() { TOGGLE_RECEIVED_CARDS: () => setIsVisible(!isVisible), - VIEW_VC: (vcRef: ActorRefFrom) => { + VIEW_VC: (vcRef: ActorRefFrom) => { return myVcservice.send(MyVcsTabEvents.VIEW_VC(vcRef)); }, isViewingVc, selectedVc, + vc, activeTab: 1, DISMISS_MODAL: () => service.send(HomeScreenEvents.DISMISS_MODAL()), REFRESH: () => ReceivedVcsService.send(ReceivedVcsTabEvents.REFRESH()), diff --git a/screens/Home/ReceivedVcsTabMachine.ts b/screens/Home/ReceivedVcsTabMachine.ts index e20393b8..cac9ca8f 100644 --- a/screens/Home/ReceivedVcsTabMachine.ts +++ b/screens/Home/ReceivedVcsTabMachine.ts @@ -1,7 +1,7 @@ import {ActorRefFrom, EventFrom, sendParent} from 'xstate'; import {createModel} from 'xstate/lib/model'; -import {ExistingMosipVCItemMachine} from '../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine'; import {AppServices} from '../../shared/GlobalContext'; +import {VCItemMachine} from '../../machines/VerifiableCredential/VCItemMachine/VCItemMachine'; const model = createModel( { @@ -10,9 +10,7 @@ const model = createModel( }, { events: { - VIEW_VC: ( - vcItemActor: ActorRefFrom, - ) => ({ + VIEW_VC: (vcItemActor: ActorRefFrom) => ({ vcItemActor, }), REFRESH: () => ({}), diff --git a/screens/Home/ViewVcModal.tsx b/screens/Home/ViewVcModal.tsx index 1a31a731..fb4fa059 100644 --- a/screens/Home/ViewVcModal.tsx +++ b/screens/Home/ViewVcModal.tsx @@ -1,5 +1,5 @@ -import React, {useEffect} from 'react'; -import {Column, Row} from '../../components/ui'; +import React from 'react'; +import {Row} from '../../components/ui'; import {Modal} from '../../components/ui/Modal'; import {MessageOverlay} from '../../components/MessageOverlay'; import {ToastItem} from '../../components/ui/ToastItem'; @@ -8,12 +8,6 @@ import {useTranslation} from 'react-i18next'; import {OtpVerificationModal} from './MyVcs/OtpVerificationModal'; import {BindingVcWarningOverlay} from './MyVcs/BindingVcWarningOverlay'; import {VcDetailsContainer} from '../../components/VC/VcDetailsContainer'; -import { - getEndEventData, - getErrorEventData, - sendEndEvent, - sendErrorEvent, -} from '../../shared/telemetry/TelemetryUtils'; import {TelemetryConstants} from '../../shared/telemetry/TelemetryConstants'; import {BannerNotificationContainer} from '../../components/BannerNotificationContainer'; import {Icon} from 'react-native-elements'; @@ -23,7 +17,6 @@ import {HelpScreen} from '../../components/HelpScreen'; import {Pressable} from 'react-native'; import {KebabPopUp} from '../../components/KebabPopUp'; import {SvgImage} from '../../components/ui/svg'; -import {faceImageSource} from '../../components/VcItemContainerProfileImage'; import {VCMetadata} from '../../shared/VCMetadata'; import {WalletBinding} from './MyVcs/WalletBinding'; import {RemoveVcWarningOverlay} from './MyVcs/RemoveVcWarningOverlay'; @@ -32,42 +25,7 @@ import {HistoryTab} from './MyVcs/HistoryTab'; export const ViewVcModal: React.FC = props => { const {t} = useTranslation('ViewVcModal'); const controller = useViewVcModal(props); - - useEffect(() => { - let error = controller.walletBindingError; - if (error) { - error = controller.bindingAuthFailedError - ? controller.bindingAuthFailedError + '-' + error - : error; - sendErrorEvent( - getErrorEventData( - TelemetryConstants.FlowType.vcActivation, - TelemetryConstants.ErrorId.activationFailed, - error, - ), - ); - sendEndEvent( - getEndEventData( - TelemetryConstants.FlowType.vcActivation, - TelemetryConstants.EndEventStatus.failure, - ), - ); - } - }, [controller.walletBindingError]); - - let selectedVcContext = props.vcItemActor.getSnapshot()?.context; - - const credential = new VCMetadata( - selectedVcContext?.vcMetadata, - ).isFromOpenId4VCI() - ? selectedVcContext?.verifiableCredential?.credential - : selectedVcContext?.verifiableCredential; - - const getVcProfileImage = faceImageSource({ - vcMetadata: new VCMetadata(selectedVcContext?.vcMetadata), - context: props.vcItemActor.getSnapshot()?.context, - credential: credential, - }); + const profileImage = controller.verifiableCredentialData.face; const headerRight = flow => { return flow === 'downloadedVc' ? ( @@ -91,7 +49,7 @@ export const ViewVcModal: React.FC = props => { = props => { } onDismiss={() => props.vcItemActor.send('DISMISS')} service={props.vcItemActor} - vcHasImage={getVcProfileImage !== undefined} + vcHasImage={profileImage !== undefined} /> @@ -117,11 +75,12 @@ export const ViewVcModal: React.FC = props => { headerElevation={2}> {controller.isAcceptingBindingOtp && ( @@ -132,8 +91,8 @@ export const ViewVcModal: React.FC = props => { onInputDone={controller.inputOtp} error={controller.otpError} resend={controller.RESEND_OTP} - phone={controller.isPhoneNumber} - email={controller.isEmail} + phone={controller.isCommunicationDetails.phoneNumber} + email={controller.isCommunicationDetails.emailId} flow={TelemetryConstants.FlowType.vcActivation} /> )} @@ -163,18 +122,20 @@ export const ViewVcModal: React.FC = props => { ); diff --git a/screens/Home/ViewVcModalController.ts b/screens/Home/ViewVcModalController.ts index a1cf1f00..4af69de9 100644 --- a/screens/Home/ViewVcModalController.ts +++ b/screens/Home/ViewVcModalController.ts @@ -1,32 +1,31 @@ import {useMachine, useSelector} from '@xstate/react'; import {useContext, useEffect, useState} from 'react'; import {ActorRefFrom} from 'xstate'; -import {useTranslation} from 'react-i18next'; import NetInfo from '@react-native-community/netinfo'; import {ModalProps} from '../../components/ui/Modal'; import {GlobalContext} from '../../shared/GlobalContext'; import { - selectOtpError, - selectWalletBindingError, - selectEmptyWalletBindingId, - selectShowWalletBindingError, - selectWalletBindingSuccess, - selectBindingAuthFailedError, selectAcceptingBindingOtp, - selectWalletBindingInProgress, + selectBindingAuthFailedError, selectBindingWarning, - selectIsPhoneNumber, - selectIsEmail, -} from '../../machines/VCItemMachine/commonSelectors'; -import { - selectIsAcceptingOtpInput, + selectIsCommunicationDetails, + selectOtpError, + selectShowWalletBindingError, selectVc, - ExistingMosipVCItemEvents, - ExistingMosipVCItemMachine, - selectRequestBindingOtp, -} from '../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine'; + selectCredential, + selectVerifiableCredentialData, + selectWalletBindingError, + selectWalletBindingInProgress, + selectWalletBindingResponse, + selectWalletBindingSuccess, +} from '../../machines/VerifiableCredential/VCItemMachine/VCItemSelectors'; import {selectPasscode} from '../../machines/auth'; import {biometricsMachine, selectIsSuccess} from '../../machines/biometrics'; +import { + VCItemEvents, + VCItemMachine, +} from '../../machines/VerifiableCredential/VCItemMachine/VCItemMachine'; +import {selectIsAcceptingOtpInput} from './MyVcs/AddVcModalMachine'; export function useViewVcModal({vcItemActor, isVisible}: ViewVcModalProps) { const [toastVisible, setToastVisible] = useState(false); @@ -62,9 +61,9 @@ export function useViewVcModal({vcItemActor, isVisible}: ViewVcModalProps) { const netInfoFetch = (otp: string) => { NetInfo.fetch().then(state => { if (state.isConnected) { - vcItemActor.send(ExistingMosipVCItemEvents.INPUT_OTP(otp)); + vcItemActor.send(VCItemEvents.INPUT_OTP(otp)); } else { - vcItemActor.send(ExistingMosipVCItemEvents.DISMISS()); + vcItemActor.send(VCItemEvents.DISMISS()); showToast('Request network failed'); } }); @@ -77,27 +76,31 @@ export function useViewVcModal({vcItemActor, isVisible}: ViewVcModalProps) { }, [reAuthenticating, isSuccessBio, otError, vc]); useEffect(() => { - vcItemActor.send(ExistingMosipVCItemEvents.REFRESH()); + vcItemActor.send(VCItemEvents.REFRESH()); }, [isVisible]); return { error, message, toastVisible, - vc, + credential: useSelector(vcItemActor, selectCredential), + verifiableCredentialData: useSelector( + vcItemActor, + selectVerifiableCredentialData, + ), otpError: useSelector(vcItemActor, selectOtpError), bindingAuthFailedError: useSelector( vcItemActor, selectBindingAuthFailedError, ), reAuthenticating, + isAcceptingOtpInput: useSelector(vcItemActor, selectIsAcceptingOtpInput), storedPasscode: useSelector(authService, selectPasscode), - isBindingOtp: useSelector(vcItemActor, selectRequestBindingOtp), isAcceptingBindingOtp: useSelector(vcItemActor, selectAcceptingBindingOtp), - walletBindingError: useSelector(vcItemActor, selectWalletBindingError), - isWalletBindingPending: useSelector( + walletBindingResponse: useSelector( vcItemActor, - selectEmptyWalletBindingId, + selectWalletBindingResponse, ), + walletBindingError: useSelector(vcItemActor, selectWalletBindingError), isWalletBindingInProgress: useSelector( vcItemActor, selectWalletBindingInProgress, @@ -105,31 +108,29 @@ export function useViewVcModal({vcItemActor, isVisible}: ViewVcModalProps) { isBindingError: useSelector(vcItemActor, selectShowWalletBindingError), isBindingSuccess: useSelector(vcItemActor, selectWalletBindingSuccess), isBindingWarning: useSelector(vcItemActor, selectBindingWarning), - isPhoneNumber: useSelector(vcItemActor, selectIsPhoneNumber), - isEmail: useSelector(vcItemActor, selectIsEmail), - + isCommunicationDetails: useSelector( + vcItemActor, + selectIsCommunicationDetails, + ), setReAuthenticating, onError, addtoWallet: () => { - vcItemActor.send(ExistingMosipVCItemEvents.ADD_WALLET_BINDING_ID()); + vcItemActor.send(VCItemEvents.ADD_WALLET_BINDING_ID()); }, inputOtp: (otp: string) => { netInfoFetch(otp); }, - ADD_WALLET: () => - vcItemActor.send(ExistingMosipVCItemEvents.ADD_WALLET_BINDING_ID()), onSuccess, - DISMISS: () => vcItemActor.send(ExistingMosipVCItemEvents.DISMISS()), - INPUT_OTP: (otp: string) => - vcItemActor.send(ExistingMosipVCItemEvents.INPUT_OTP(otp)), - RESEND_OTP: () => vcItemActor.send(ExistingMosipVCItemEvents.RESEND_OTP()), - CANCEL: () => vcItemActor.send(ExistingMosipVCItemEvents.CANCEL()), - CONFIRM: () => vcItemActor.send(ExistingMosipVCItemEvents.CONFIRM()), + DISMISS: () => vcItemActor.send(VCItemEvents.DISMISS()), + INPUT_OTP: (otp: string) => vcItemActor.send(VCItemEvents.INPUT_OTP(otp)), + RESEND_OTP: () => vcItemActor.send(VCItemEvents.RESEND_OTP()), + CANCEL: () => vcItemActor.send(VCItemEvents.CANCEL()), + CONFIRM: () => vcItemActor.send(VCItemEvents.CONFIRM()), }; } export interface ViewVcModalProps extends ModalProps { - vcItemActor: ActorRefFrom; + vcItemActor: ActorRefFrom; onDismiss: () => void; activeTab: Number; flow: string; diff --git a/screens/QrLogin/QrLogin.tsx b/screens/QrLogin/QrLogin.tsx index 81f38549..efd1ba6a 100644 --- a/screens/QrLogin/QrLogin.tsx +++ b/screens/QrLogin/QrLogin.tsx @@ -12,7 +12,7 @@ import {Icon} from 'react-native-elements'; import {View} from 'react-native'; import {FaceVerificationAlertOverlay} from '../Scan/FaceVerificationAlertOverlay'; import {Error} from '../../components/ui/Error'; -import { SvgImage } from '../../components/ui/svg'; +import {SvgImage} from '../../components/ui/svg'; export const QrLogin: React.FC = props => { const controller = useQrLogin(props); @@ -50,11 +50,15 @@ export const QrLogin: React.FC = props => { /> ; @@ -40,50 +40,29 @@ export function useQrLogin({service}: QrLoginProps) { const {appService} = useContext(GlobalContext); const navigation = useNavigation(); - const vcService = appService.children.get('vc'); + const vcService = appService.children.get('vc')!!; const [selectedIndex, setSelectedIndex] = useState(null); - const SELECT_VC = (vc: VC) => service.send(QrLoginEvents.SELECT_VC(vc)); - - const SELECT_CONSENT = (value: boolean, claim: string) => { - service.send(QrLoginEvents.TOGGLE_CONSENT_CLAIM(value, claim)); - }; - - const isShare = useSelector(service, selectIsSharing); - - return { - SELECT_VC_ITEM: - (index: number) => - ( - vcRef: ActorRefFrom< - typeof EsignetMosipVCItemMachine | typeof EsignetMosipVCItemMachine - >, - ) => { - setSelectedIndex(index); - const vcData = vcRef.getSnapshot().context; - SELECT_VC(vcData); - }, - - isFaceVerificationConsent: useSelector(service, selectIsFaceVerificationConsent), - shareableVcsMetadata: useSelector(vcService, selectBindedVcsMetadata), - selectedVc: useSelector(service, selectSelectedVc), + isFaceVerificationConsent: useSelector( + service, + selectIsFaceVerificationConsent, + ), linkTransactionResponse: useSelector( service, selectLinkTransactionResponse, ), + shareableVcsMetadata: useSelector(vcService, selectBindedVcsMetadata), + verifiableCredentialData: useSelector( + service, + selectVerifiableCredentialData, + ), domainName: useSelector(service, selectDomainName), logoUrl: useSelector(service, selectLogoUrl), essentialClaims: useSelector(service, selectEssentialClaims), voluntaryClaims: useSelector(service, selectVoluntaryClaims), clientName: useSelector(service, selectClientName), error: useSelector(service, selectErrorMessage), - - isShare, - - selectedIndex, - SELECT_VC, - SELECT_CONSENT, - FACE_VERIFICATION_CONSENT: (isConsentGiven: boolean) => service.send(QrLoginEvents.FACE_VERIFICATION_CONSENT(isConsentGiven)), + selectCredential: useSelector(service, selectCredential), isWaitingForData: useSelector(service, selectIsWaitingForData), isShowingVcList: useSelector(service, selectIsShowingVcList), isLinkTransaction: useSelector(service, selectIsLinkTransaction), @@ -95,14 +74,25 @@ export function useQrLogin({service}: QrLoginProps) { isVerifyingIdentity: useSelector(service, selectIsisVerifyingIdentity), isInvalidIdentity: useSelector(service, selectIsInvalidIdentity), isVerifyingSuccesful: useSelector(service, selectIsVerifyingSuccesful), - + isShare: useSelector(service, selectIsSharing), + selectedIndex, + SELECT_CONSENT: (value: boolean, claim: string) => { + service.send(QrLoginEvents.TOGGLE_CONSENT_CLAIM(value, claim)); + }, + SELECT_VC_ITEM: + (index: number) => (vcRef: ActorRefFrom) => { + setSelectedIndex(index); + const vcData = vcRef.getSnapshot().context; + service.send(QrLoginEvents.SELECT_VC(vcData)); + }, + FACE_VERIFICATION_CONSENT: (isConsentGiven: boolean) => + service.send(QrLoginEvents.FACE_VERIFICATION_CONSENT(isConsentGiven)), DISMISS: () => service.send(QrLoginEvents.DISMISS()), SCANNING_DONE: (qrCode: string) => service.send(QrLoginEvents.SCANNING_DONE(qrCode)), CONFIRM: () => service.send(QrLoginEvents.CONFIRM()), VERIFY: () => service.send(QrLoginEvents.VERIFY()), CANCEL: () => service.send(QrLoginEvents.CANCEL()), - FACE_VALID: () => service.send(QrLoginEvents.FACE_VALID()), FACE_INVALID: () => service.send(QrLoginEvents.FACE_INVALID()), RETRY_VERIFICATION: () => service.send(QrLoginEvents.RETRY_VERIFICATION()), diff --git a/screens/Request/ReceiveVcScreen.tsx b/screens/Request/ReceiveVcScreen.tsx index 511a5d9b..970ee4eb 100644 --- a/screens/Request/ReceiveVcScreen.tsx +++ b/screens/Request/ReceiveVcScreen.tsx @@ -1,14 +1,11 @@ import React from 'react'; import {useTranslation} from 'react-i18next'; import {DeviceInfoList} from '../../components/DeviceInfoList'; -import {Button, Column, Row, Text} from '../../components/ui'; +import {Button, Column, Text} from '../../components/ui'; import {Theme} from '../../components/ui/styleUtils'; import {useReceiveVcScreen} from './ReceiveVcScreenController'; import {VerifyIdentityOverlay} from '../VerifyIdentityOverlay'; -import { - ErrorMessageOverlay, - MessageOverlay, -} from '../../components/MessageOverlay'; +import {MessageOverlay} from '../../components/MessageOverlay'; import {useOverlayVisibleAfterTimeout} from '../../shared/hooks/useOverlayVisibleAfterTimeout'; import {VcDetailsContainer} from '../../components/VC/VcDetailsContainer'; import {SharingStatusModal} from '../Scan/SharingStatusModal'; @@ -34,7 +31,8 @@ export const ReceiveVcScreen: React.FC = () => { {t('header')} @@ -50,7 +48,8 @@ export const ReceiveVcScreen: React.FC = () => { )} { return ( { export function useScanLayout() { const {t} = useTranslation('ScanScreen'); const {appService} = useContext(GlobalContext); - const scanService = appService.children.get('scan'); + const scanService = appService.children.get('scan')!!; const navigation = useNavigation(); const isLocationDisabled = useSelector(scanService, selectIsLocationDisabled); @@ -68,8 +69,12 @@ export function useScanLayout() { scanService, selectIsVerifyingIdentity, ); - const selectedVc = useSelector(scanService, selectSelectedVc); const bleError = useSelector(scanService, selectBleError); + const credential = useSelector(scanService, selectCredential); + const verifiableCredentialData = useSelector( + scanService, + selectVerifiableCredentialData, + ); const locationError = {message: '', button: ''}; @@ -282,6 +287,8 @@ export function useScanLayout() { ]); return { + credential, + verifiableCredentialData, isInvalid, isReviewing, isDone, @@ -302,7 +309,6 @@ export function useScanLayout() { flowType, isVerifyingIdentity, isInvalidIdentity, - selectedVc, FACE_INVALID, FACE_VALID, RETRY_VERIFICATION, diff --git a/screens/Scan/ScanScreenController.ts b/screens/Scan/ScanScreenController.ts index 595c5f02..3ea26d66 100644 --- a/screens/Scan/ScanScreenController.ts +++ b/screens/Scan/ScanScreenController.ts @@ -1,7 +1,7 @@ import {useSelector} from '@xstate/react'; import {useContext} from 'react'; import {useTranslation} from 'react-i18next'; -import {selectShareableVcsMetadata} from '../../machines/VCItemMachine/vc'; +import {selectShareableVcsMetadata} from '../../machines/VerifiableCredential/VCMetaMachine/vc'; import {GlobalContext} from '../../shared/GlobalContext'; import { selectIsLocationDenied, diff --git a/screens/Scan/SelectVcOverlayController.ts b/screens/Scan/SelectVcOverlayController.ts index f8462d15..13e16cb7 100644 --- a/screens/Scan/SelectVcOverlayController.ts +++ b/screens/Scan/SelectVcOverlayController.ts @@ -1,13 +1,13 @@ import {useState} from 'react'; import {ActorRefFrom} from 'xstate'; -import {ExistingMosipVCItemMachine} from '../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine'; -import {VC} from '../../types/VC/ExistingMosipVC/vc'; +import {VC} from '../../types/VC/vc'; import {VCMetadata} from '../../shared/VCMetadata'; +import {VCItemMachine} from '../../machines/VerifiableCredential/VCItemMachine/VCItemMachine'; export function useSelectVcOverlay(props: SelectVcOverlayProps) { const [selectedIndex, setSelectedIndex] = useState(null); const [selectedVcRef, setSelectedVcRef] = - useState>(null); + useState>(null); return { selectVcItem, @@ -25,7 +25,7 @@ export function useSelectVcOverlay(props: SelectVcOverlayProps) { }; function selectVcItem(index: number) { - return (vcRef: ActorRefFrom) => { + return (vcRef: ActorRefFrom) => { setSelectedIndex(index); setSelectedVcRef(vcRef); }; diff --git a/screens/Scan/SendVcScreen.tsx b/screens/Scan/SendVcScreen.tsx index 66ba7df0..3456e961 100644 --- a/screens/Scan/SendVcScreen.tsx +++ b/screens/Scan/SendVcScreen.tsx @@ -1,25 +1,24 @@ import React, {useContext, useEffect, useRef} from 'react'; import {useTranslation} from 'react-i18next'; -import {Button, Column, Row, Text} from '../../components/ui'; +import {Button, Column, Text} from '../../components/ui'; import {Theme} from '../../components/ui/styleUtils'; import {useSendVcScreen} from './SendVcScreenController'; import {VerifyIdentityOverlay} from '../VerifyIdentityOverlay'; import {BackHandler} from 'react-native'; import {useInterpret} from '@xstate/react'; -import {createExistingMosipVCItemMachine} from '../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine'; import {GlobalContext} from '../../shared/GlobalContext'; import {useFocusEffect} from '@react-navigation/native'; import {VcItemContainer} from '../../components/VC/VcItemContainer'; import {VCMetadata} from '../../shared/VCMetadata'; -import {createEsignetMosipVCItemMachine} from '../../machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine'; +import {createVCItemMachine} from '../../machines/VerifiableCredential/VCItemMachine/VCItemMachine'; import { getImpressionEventData, sendImpressionEvent, } from '../../shared/telemetry/TelemetryUtils'; import {TelemetryConstants} from '../../shared/telemetry/TelemetryConstants'; import { - VCItemContainerFlowType, getVCsOrderedByPinStatus, + VCItemContainerFlowType, } from '../../shared/Utils'; import {Issuers} from '../../shared/openId4VCI/Utils'; import {FaceVerificationAlertOverlay} from './FaceVerificationAlertOverlay'; @@ -38,15 +37,10 @@ export const SendVcScreen: React.FC = () => { if (shareableVcsMetadataOrderedByPinStatus?.length > 0) { const vcMetadata = shareableVcsMetadataOrderedByPinStatus[0]; const firstVCMachine = useRef( - VCMetadata.fromVC(vcMetadata).isFromOpenId4VCI() - ? createEsignetMosipVCItemMachine( - appService.getSnapshot().context.serviceRefs, - vcMetadata, - ) - : createExistingMosipVCItemMachine( - appService.getSnapshot().context.serviceRefs, - vcMetadata, - ), + createVCItemMachine( + appService.getSnapshot().context.serviceRefs, + vcMetadata, + ), ); service = useInterpret(firstVCMachine.current); @@ -108,20 +102,17 @@ export const SendVcScreen: React.FC = () => { - {!controller.selectedVc.shouldVerifyPresence && - controller.selectedVc?.vcMetadata && - [Issuers.Mosip, Issuers.ESignet].indexOf( - VCMetadata.fromVcMetadataString(controller.selectedVc.vcMetadata) - .issuer, - ) !== -1 && ( -