diff --git a/.env b/.env index 3c462bb6..3bd290d7 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-inji.mosip.net +MIMOTO_HOST=https://api.qa-inji1.mosip.net -ESIGNET_HOST=https://api.qa-inji.mosip.net +ESIGNET_HOST=https://api.qa-inji1.mosip.net OBSRV_HOST = https://dataset-api.obsrv.mosip.net @@ -17,3 +17,6 @@ DEBUG_MODE=false #supported languages( en, fil, ar, hi, kn, ta) APPLICATION_LANGUAGE=en + +#Card Templatization - Render Claims Dynamically. +CARD_TEMPLATIZATION=false diff --git a/components/VC/MosipVCItem/MosipVCItemActivationStatus.tsx b/components/VC/MosipVCItem/MosipVCItemActivationStatus.tsx index 4e7e5aca..29549c6d 100644 --- a/components/VC/MosipVCItem/MosipVCItemActivationStatus.tsx +++ b/components/VC/MosipVCItem/MosipVCItemActivationStatus.tsx @@ -1,10 +1,10 @@ import React from 'react'; import {useTranslation} from 'react-i18next'; -import {Dimensions} from 'react-native'; import {Icon} from 'react-native-elements'; import {VerifiableCredential} from '../../../types/VC/ExistingMosipVC/vc'; import {Row, Text} from '../../ui'; import {Theme} from '../../ui/styleUtils'; +import {View} from 'react-native'; const WalletUnverifiedIcon: React.FC = () => { return ( @@ -41,17 +41,21 @@ const WalletUnverifiedActivationDetails: React.FC< const {t} = useTranslation('VcDetails'); return ( - {props.verifiableCredential && } - + + {props.verifiableCredential && } + + + + ); }; @@ -62,19 +66,23 @@ const WalletVerifiedActivationDetails: React.FC< const {t} = useTranslation('WalletBinding'); return ( - - + + + + + + ); }; diff --git a/components/VC/VcDetailsContainer.tsx b/components/VC/VcDetailsContainer.tsx index 51fe02e2..e55a1dd9 100644 --- a/components/VC/VcDetailsContainer.tsx +++ b/components/VC/VcDetailsContainer.tsx @@ -1,12 +1,16 @@ import React from 'react'; import { - MosipVCItemDetails, - ExistingMosipVCItemDetailsProps, EsignetMosipVCItemDetailsProps, + ExistingMosipVCItemDetailsProps, + MosipVCItemDetails, } from './MosipVCItem/MosipVCItemDetails'; +import {CARD_TEMPLATIZATION} from 'react-native-dotenv'; +import {VCDetailView} from './Views/VCDetailView'; + export const VcDetailsContainer: React.FC< ExistingMosipVCItemDetailsProps | EsignetMosipVCItemDetailsProps > = props => { + if (CARD_TEMPLATIZATION === 'true') return ; return ; }; diff --git a/components/VC/VcItemContainer.tsx b/components/VC/VcItemContainer.tsx index 2dd0377b..98d26255 100644 --- a/components/VC/VcItemContainer.tsx +++ b/components/VC/VcItemContainer.tsx @@ -1,12 +1,15 @@ import React from 'react'; import { EsignetMosipVCItemProps, - MosipVCItem, ExistingMosipVCItemProps, + MosipVCItem, } from './MosipVCItem/MosipVCItem'; +import {CARD_TEMPLATIZATION} from 'react-native-dotenv'; +import {VCCardView} from './Views/VCCardView'; export const VcItemContainer: React.FC< ExistingMosipVCItemProps | EsignetMosipVCItemProps > = props => { + if (CARD_TEMPLATIZATION === 'true') return ; return ; }; diff --git a/components/VC/Views/VCCardView.tsx b/components/VC/Views/VCCardView.tsx new file mode 100644 index 00000000..d6ed30bf --- /dev/null +++ b/components/VC/Views/VCCardView.tsx @@ -0,0 +1,166 @@ +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 {Row} from '../../ui'; +import {KebabPopUp} from '../../KebabPopUp'; +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 {MosipVCItemActivationStatus} from '../MosipVCItem/MosipVCItemActivationStatus'; +import {useVcItemController} from '../MosipVCItem/VcItemController'; +import {getCredentialIssuersWellKnownConfig} from '../../../shared/openId4VCI/Utils'; +import { + CARD_VIEW_ADD_ON_FIELDS, + CARD_VIEW_DEFAULT_FIELDS, +} from '../../../shared/constants'; +import {isVCLoaded} from '../common/VCUtils'; + +export const VCCardView: React.FC< + ExistingMosipVCItemProps | EsignetMosipVCItemProps +> = props => { + let { + service, + context, + verifiableCredential, + emptyWalletBindingId, + isKebabPopUp, + isSavingFailedInIdle, + storeErrorTranslationPath, + generatedOn, + + DISMISS, + KEBAB_POPUP, + } = useVcItemController(props); + + let formattedDate = + generatedOn && format(new Date(generatedOn), 'MM/dd/yyyy'); + + useEffect(() => { + service.send( + ExistingMosipVCItemEvents.UPDATE_VC_METADATA(props.vcMetadata), + ); + }, [props.vcMetadata]); + + const credential = props.isDownloading + ? null + : props.vcMetadata.isFromOpenId4VCI() + ? verifiableCredential?.credential + : verifiableCredential; + + const [fields, setFields] = useState([]); + const [wellknown, setWellknown] = useState(null); + useEffect(() => { + getCredentialIssuersWellKnownConfig( + props?.vcMetadata.issuer, + verifiableCredential?.wellKnown, + CARD_VIEW_DEFAULT_FIELDS, + ).then(response => { + setWellknown(response.wellknown); + setFields(response.fields.slice(0, 1).concat(CARD_VIEW_ADD_ON_FIELDS)); + }); + }, [verifiableCredential?.wellKnown]); + + if (!isVCLoaded(verifiableCredential, fields)) { + return ; + } + + return ( + + props.onPress(service)} + disabled={!verifiableCredential} + style={ + props.selected + ? Theme.Styles.selectedBindedVc + : Theme.Styles.closeCardBgContainer + }> + props.onPress(service)} + isDownloading={props.isDownloading} + /> + + {props.isSharingVc ? null : ( + + + + + + + + + + + + )} + + + + ); +}; + +export interface ExistingMosipVCItemProps { + vcMetadata: VCMetadata; + margin?: string; + selectable?: boolean; + selected?: boolean; + showOnlyBindedVc?: boolean; + onPress?: (vcRef?: ActorRefFrom) => void; + onShow?: (vcRef?: ActorRefFrom) => void; + isSharingVc?: boolean; + isDownloading?: boolean; + isPinned?: boolean; +} + +export interface EsignetMosipVCItemProps { + vcMetadata: VCMetadata; + margin?: string; + selectable?: boolean; + selected?: boolean; + showOnlyBindedVc?: boolean; + onPress?: (vcRef?: ActorRefFrom) => void; + onShow?: (vcRef?: ActorRefFrom) => void; + isSharingVc?: boolean; + isDownloading?: boolean; + isPinned?: boolean; +} diff --git a/components/VC/Views/VCCardViewContent.tsx b/components/VC/Views/VCCardViewContent.tsx new file mode 100644 index 00000000..ff9aec77 --- /dev/null +++ b/components/VC/Views/VCCardViewContent.tsx @@ -0,0 +1,132 @@ +import React from 'react'; +import {ImageBackground} from 'react-native'; +import {VerifiableCredential} from '../../../types/VC/ExistingMosipVC/vc'; +import {Column, Row} from '../../ui'; +import {Theme} from '../../ui/styleUtils'; +import {CheckBox, Icon} from 'react-native-elements'; +import {SvgImage} from '../../ui/svg'; +import { + fieldItemIterator, + getIssuerLogo, + isVCLoaded, + setBackgroundColour, +} from '../common/VCUtils'; +import VerifiedIcon from '../../VerifiedIcon'; +import {VCItemField} from '../common/VCItemField'; +import {useTranslation} from 'react-i18next'; + +export const VCCardViewContent: React.FC< + ExistingMosipVCItemContentProps | EsignetMosipVCItemContentProps +> = props => { + const {t} = useTranslation('VcDetails'); + const selectableOrCheck = props.selectable ? ( + + } + uncheckedIcon={ + + } + onPress={() => props.onPress()} + /> + ) : null; + + return ( + + + + + {SvgImage.VcItemContainerProfileImage(props, props.credential)} + + {fieldItemIterator( + props.fields.slice(0, 2), + props.credential, + props.wellknown, + props, + )} + + + {props.credential ? selectableOrCheck : null} + + {fieldItemIterator( + props.fields.slice(2), + props.credential, + props.wellknown, + props, + )} + + + ) + } + wellknown={props.wellknown} + verifiableCredential={props.credential} + /> + + {!isVCLoaded(props.credential, props.fields) + ? null + : getIssuerLogo( + props.vcMetadata.isFromOpenId4VCI(), + props.verifiableCredential?.issuerLogo, + )} + + + + + ); +}; + +export interface ExistingMosipVCItemContentProps { + context: any; + verifiableCredential: VerifiableCredential; + credential: VerifiableCredential; + fields: []; + wellknown: {}; + generatedOn: string; + selectable: boolean; + selected: boolean; + isPinned?: boolean; + service: any; + onPress?: () => void; + isDownloading?: boolean; +} + +export interface EsignetMosipVCItemContentProps { + context: any; + credential: VerifiableCredential; + fields: []; + wellknown: {}; + generatedOn: string; + selectable: boolean; + selected: boolean; + isPinned?: boolean; + service: any; + onPress?: () => void; + isDownloading?: boolean; +} + +VCCardViewContent.defaultProps = { + isPinned: false, +}; diff --git a/components/VC/Views/VCDetailView.tsx b/components/VC/Views/VCDetailView.tsx new file mode 100644 index 00000000..6cd16291 --- /dev/null +++ b/components/VC/Views/VCDetailView.tsx @@ -0,0 +1,248 @@ +import {formatDistanceToNow} from 'date-fns'; +import React, {useEffect, useState} from 'react'; +import * as DateFnsLocale from 'date-fns/locale'; +import {useTranslation} from 'react-i18next'; +import {Image, ImageBackground} from 'react-native'; +import {Icon} from 'react-native-elements'; +import {VC} from '../../../types/VC/ExistingMosipVC/vc'; +import {Button, Column, Row, Text} from '../../ui'; +import {Theme} from '../../ui/styleUtils'; +import {TextItem} from '../../ui/TextItem'; +import {QrCodeOverlay} from '../../QrCodeOverlay'; +import {VCMetadata} from '../../../shared/VCMetadata'; +import { + VcIdType, + VCSharingReason, + VerifiableCredential, + VerifiablePresentation, +} from '../../../types/VC/EsignetMosipVC/vc'; +import {WalletBindingResponse} from '../../../shared/cryptoutil/cryptoUtil'; +import {logoType} from '../../../machines/issuersMachine'; +import {SvgImage} from '../../ui/svg'; +import {getCredentialIssuersWellKnownConfig} from '../../../shared/openId4VCI/Utils'; +import { + DETAIL_VIEW_ADD_ON_FIELDS, + DETAIL_VIEW_DEFAULT_FIELDS, +} from '../../../shared/constants'; +import { + fieldItemIterator, + isVCLoaded, + setBackgroundColour, +} from '../common/VCUtils'; +import {ActivityIndicator} from '../../ui/ActivityIndicator'; + +const getIssuerLogo = (isOpenId4VCI: boolean, issuerLogo: logoType) => { + if (isOpenId4VCI) { + return ( + {issuerLogo?.alt_text} + ); + } + return SvgImage.MosipLogo(Theme.Styles.vcDetailsLogo); +}; + +const getProfileImage = ( + props: ExistingMosipVCItemDetailsProps | EsignetMosipVCItemDetailsProps, + verifiableCredential, + isOpenId4VCI, +) => { + if (isOpenId4VCI) { + if (verifiableCredential?.credentialSubject.face) { + return {uri: verifiableCredential?.credentialSubject.face}; + } + } else { + if (props.vc?.credential?.biometrics?.face) { + return {uri: props.vc?.credential.biometrics.face}; + } + } + return ; +}; + +export const VCDetailView: React.FC< + ExistingMosipVCItemDetailsProps | EsignetMosipVCItemDetailsProps +> = 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; + + let [fields, setFields] = useState([]); + const [wellknown, setWellknown] = useState(null); + useEffect(() => { + getCredentialIssuersWellKnownConfig( + VCMetadata.fromVC(props.vc.vcMetadata).issuer, + props.vc?.verifiableCredential?.wellKnown, + DETAIL_VIEW_DEFAULT_FIELDS, + ).then(response => { + setWellknown(response.wellknown); + setFields(response.fields.concat(DETAIL_VIEW_ADD_ON_FIELDS)); + }); + }, [props.verifiableCredential?.wellKnown]); + + if (!isVCLoaded(verifiableCredential, fields)) { + return ; + } + + return ( + + + + + + + + + {issuerLogo} + + + {fieldItemIterator(fields, verifiableCredential, wellknown, props)} + + + + + {props.vc?.reason?.length > 0 && ( + + {t('reasonForSharing')} + + )} + + {props.vc?.reason?.map((reason, index) => ( + + ))} + + {props.activeTab !== 1 ? ( + props.isBindingPending ? ( + + + + + {t('offlineAuthDisabledHeader')} + + + + {t('offlineAuthDisabledMessage')} + + +