Files
inji-wallet/components/VC/MosipVCItem/MosipVCItemDetails.tsx
Anil kumar M 27e2f07cd9 Fix(INJI-431): Resolved that UI would support all screen sizes(folded-devices) & fixed language translation issues. (#920)
* fix(MOSIP-29275): make usesCleartextTraffic to false for more secure communication (#886)

* fix(MOSIP-29275): make usesCleartextTraffic to false for more secure communications

* fix(MOSIP-29275): override usesCleartextTraffic to false in main manifest

---------

Signed-off-by: Swati Goel <meet2swati@gmail.com>
Co-authored-by: Swati Goel <meet2swati@gmail.com>
Signed-off-by: anil_majji <majjianilkumar050@gmail.com>

* MOSIP-29698 (#891)

Signed-off-by: anil_majji <majjianilkumar050@gmail.com>

* Inji-409: debugging setup fix (#893)

* feat(inji-400): update package-lock.json

* feat(inji-400): move flipper config to separate file from app.ts

Signed-off-by: anil_majji <majjianilkumar050@gmail.com>

* feat(inji-398): enable cleartextTrafficPermitted in debug mode (#895)

Signed-off-by: anil_majji <majjianilkumar050@gmail.com>

* fix(INJI-397)error message for deleted vcfile (#875)

* fix(INJI-397)error message for deleted vcfile

* fix(INJI-397)refactor:log activity event

* fix(INJI-397)refactor:log activity event

* fix(INJI-397)refactor:comparing VCMetadata

Signed-off-by: anil_majji <majjianilkumar050@gmail.com>

* fix(MOSIP-29272): remove paste option for pin inputs (#894)

Signed-off-by: anil_majji <majjianilkumar050@gmail.com>

* Test runner support to run jar MOSIP-29698 (#898)

* MOSIP-29698

* Test runner support to run jar MOSIP-29698

* Update TestData.json

Signed-off-by: neeharikatech <76684248+neeharikatech@users.noreply.github.com>

---------

Signed-off-by: neeharikatech <76684248+neeharikatech@users.noreply.github.com>
Signed-off-by: anil_majji <majjianilkumar050@gmail.com>

* feat(inji-406): add logs for debugging tampered vc issues (#900)

Signed-off-by: anil_majji <majjianilkumar050@gmail.com>

* Adjusted bottom tab for different screen sizes

Signed-off-by: anil_majji <majjianilkumar050@gmail.com>

* Added responsive bottom line for ID input

Signed-off-by: anil_majji <majjianilkumar050@gmail.com>

* Updated the missing texts with case sensitive

Signed-off-by: anil_majji <majjianilkumar050@gmail.com>

* Removed unused button text in Welcome screen

Signed-off-by: anil_majji <majjianilkumar050@gmail.com>

---------

Signed-off-by: Swati Goel <meet2swati@gmail.com>
Signed-off-by: anil_majji <majjianilkumar050@gmail.com>
Signed-off-by: neeharikatech <76684248+neeharikatech@users.noreply.github.com>
Co-authored-by: Sreenadh S <32409698+sree96@users.noreply.github.com>
Co-authored-by: Swati Goel <meet2swati@gmail.com>
Co-authored-by: neeharikatech <76684248+neeharikatech@users.noreply.github.com>
Co-authored-by: Tilak Puli <34330361+tilak-puli@users.noreply.github.com>
Co-authored-by: srikanth716 <97477121+srikanth716@users.noreply.github.com>
2023-10-13 15:23:50 +05:30

475 lines
15 KiB
TypeScript

import {format, formatDistanceToNow, parse} from 'date-fns';
import React from 'react';
import * as DateFnsLocale from 'date-fns/locale';
import {useTranslation} from 'react-i18next';
import {Image, ImageBackground, View} from 'react-native';
import {Icon} from 'react-native-elements';
import {VC, CredentialSubject} from '../../../types/VC/ExistingMosipVC/vc';
import {Button, Column, Row, Text} from '../../ui';
import {Theme} from '../../ui/styleUtils';
import {TextItem} from '../../ui/TextItem';
import VerifiedIcon from '../../VerifiedIcon';
import {getLocalizedField} from '../../../i18n';
import {CREDENTIAL_REGISTRY_EDIT} from 'react-native-dotenv';
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';
const getIssuerLogo = (isOpenId4VCI: boolean, issuerLogo: string) => {
if (isOpenId4VCI) {
return <Image src={issuerLogo} style={Theme.Styles.issuerLogo} />;
}
return <Image source={Theme.MosipLogo} style={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 Theme.ProfileIcon;
};
export const MosipVCItemDetails: 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;
//Assigning the UIN and VID from the VC details to display the idtype label
const uin = verifiableCredential?.credentialSubject.UIN;
const vid = verifiableCredential?.credentialSubject.VID;
if (verifiableCredential == null) {
return <Text align="center">Loading details...</Text>;
}
return (
<Column margin="10 0 10 0">
<ImageBackground
imageStyle={{width: '100%'}}
resizeMethod="scale"
resizeMode="stretch"
style={Theme.Styles.openCardBgContainer}
source={Theme.OpenCard}>
<Row align="space-between" padding="10" margin="0 10 0 8">
<Column align="space-evenly" crossAlign="center">
<Image
source={getProfileImage(
props,
verifiableCredential,
isOpenId4VCI,
)}
style={Theme.Styles.openCardImage}
/>
<QrCodeOverlay qrCodeDetailes={String(verifiableCredential)} />
<Column margin="20 0 0 0">{issuerLogo}</Column>
</Column>
<Column align="space-evenly" padding="10">
<Column>
<Text
testID="fullNameTitle"
weight="regular"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('fullName')}
</Text>
<Text
testID="fullNameValue"
weight="semibold"
size="smaller"
color={Theme.Colors.Details}>
{getLocalizedField(
verifiableCredential?.credentialSubject.fullName,
)}
</Text>
</Column>
<Row>
<Column>
<Column margin="20 0 0 0">
<Text
testID="gender"
weight="regular"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('gender')}
</Text>
<Text
testID="genderValue"
weight="semibold"
size="smaller"
color={Theme.Colors.Details}>
{getLocalizedField(
verifiableCredential?.credentialSubject.gender,
)}
</Text>
</Column>
<Column margin="25 0 0 0">
<Text
testID="idType"
weight="regular"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('idType')}
</Text>
<Text
testID="nationalCard"
weight="bold"
size="smaller"
color={Theme.Colors.Details}>
{t('nationalCard')}
</Text>
</Column>
{uin ? (
<Column margin="25 0 0 0">
<Text
testID="uin"
weight="regular"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('uin')}
</Text>
<Text
testID="uinNumber"
weight="semibold"
size="smaller"
color={Theme.Colors.Details}>
{uin}
</Text>
</Column>
) : null}
{vid ? (
<Column margin="25 0 0 0">
<Text
testID="vid"
weight="regular"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('vid')}
</Text>
<Text
testID="vidNumber"
weight="semibold"
size="smaller"
color={Theme.Colors.Details}>
{vid}
</Text>
</Column>
) : null}
<Column margin="30 0 0 0">
<Text
testID="generatedOnTitle"
weight="regular"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('generatedOn')}
</Text>
<Text
testID="generatedOnValue"
weight="semibold"
size="smaller"
color={Theme.Colors.Details}>
{new Date(props.vc?.generatedOn).toLocaleDateString()}
</Text>
</Column>
</Column>
<Column margin="0 0 0 38">
<Column margin="20 0 0 0">
<Text
testID="dateOfBirth"
weight="regular"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('dateOfBirth')}
</Text>
<Text
testID="dateOfBirthValue"
weight="semibold"
size="smaller"
color={Theme.Colors.Details}>
{formattedDateOfBirth()}
</Text>
</Column>
<Column margin="25 0 0 0">
<Text
testID="status"
weight="regular"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('status')}
</Text>
<Row
style={{
justifyContent: 'flex-start',
alignItems: 'center',
}}>
{props.vc?.isVerified && <VerifiedIcon />}
<Text
testID="valid"
weight="semibold"
size="smaller"
color={Theme.Colors.Details}>
{t('valid')}
</Text>
</Row>
</Column>
<Column margin="92 0 0 0">
<Text
testID="phoneNumber"
weight="regular"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('phoneNumber')}
</Text>
<Text
testID="phoneNumberValue"
weight="semibold"
size="smaller"
color={Theme.Colors.Details}>
{getLocalizedField(
verifiableCredential?.credentialSubject.phone,
)}
</Text>
</Column>
</Column>
</Row>
</Column>
</Row>
<View style={Theme.Styles.hrLine}></View>
<Column padding="10">
<Column fill style={Theme.Styles.labelPart}>
<Text
testID="emailId"
weight="regular"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('email')}
</Text>
<Row>
<Text
testID="emailIdValue"
style={
verifiableCredential?.credentialSubject.email.length > 25
? {flex: 1}
: {flex: 0}
}
weight="semibold"
size="smaller"
color={Theme.Colors.Details}>
{getLocalizedField(
verifiableCredential?.credentialSubject.email,
)}
</Text>
</Row>
</Column>
<Column style={Theme.Styles.labelPart}>
<Text
testID="address"
weight="regular"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('address')}
</Text>
<Row>
<Text
testID="addressValue"
style={{flex: 1}}
weight="semibold"
size="smaller"
color={Theme.Colors.Details}>
{getFullAddress(verifiableCredential?.credentialSubject)}
</Text>
</Row>
</Column>
{CREDENTIAL_REGISTRY_EDIT === 'true' && (
<Column fill style={Theme.Styles.labelPart}>
<Text
testID="credentialRegistry"
weight="regular"
size="smaller"
color={Theme.Colors.DetailsLabel}>
{t('credentialRegistry')}
</Text>
<Text
testID="credentialRegistryValue"
weight="semibold"
size="smaller"
color={Theme.Colors.Details}>
{props.vc?.credentialRegistry}
</Text>
</Column>
)}
</Column>
</ImageBackground>
{props.vc?.reason?.length > 0 && (
<Text
testID="reasonForSharingTitle"
margin="24 24 16 24"
weight="semibold">
{t('reasonForSharing')}
</Text>
)}
{props.vc?.reason?.map((reason, index) => (
<TextItem
testID="reason"
key={index}
divider
label={formatDistanceToNow(reason.timestamp, {
addSuffix: true,
locale: DateFnsLocale[i18n.language],
})}
text={reason.message}
/>
))}
{props.activeTab !== 1 ? (
props.isBindingPending ? (
<Column style={Theme.Styles.openCardBgContainer} padding="10">
<Column margin={'0 0 4 0'} crossAlign={'flex-start'}>
<Image source={Theme.activationPending}></Image>
<Text
testID="offlineAuthDisabledHeader"
style={{flex: 1}}
weight="semibold"
size="small"
margin={'5 0 0 0'}
color={Theme.Colors.statusLabel}>
{t('offlineAuthDisabledHeader')}
</Text>
</Column>
<Text
testID="offlineAuthDisabledMessage"
style={{flex: 1, lineHeight: 17}}
weight="regular"
size="small"
margin={'3 0 0 0'}
color={Theme.Colors.statusMessage}>
{t('offlineAuthDisabledMessage')}
</Text>
<Button
testID="enableVerification"
title={t('enableVerification')}
onPress={props.onBinding}
type="gradient"
styles={{width: '100%'}}
/>
</Column>
) : (
<Column style={Theme.Styles.openCardBgContainer} padding="10">
<Row crossAlign="center">
<Icon
name="verified-user"
color={Theme.Colors.VerifiedIcon}
size={28}
containerStyle={{marginStart: 4, bottom: 1}}
/>
<Text
testID="profileAuthenticated"
numLines={1}
color={Theme.Colors.statusLabel}
weight="bold"
size="smaller"
margin="10 10 10 10"
children={t('profileAuthenticated')}></Text>
</Row>
</Column>
)
) : (
<></>
)}
</Column>
);
function formattedDateOfBirth() {
const dateOfBirth = verifiableCredential?.credentialSubject.dateOfBirth;
const formatString =
dateOfBirth.split('/').length === 1 ? 'yyyy' : 'yyyy/MM/dd';
const parsedDate = parse(dateOfBirth, formatString, new Date());
return format(parsedDate, 'MM/dd/yyyy');
}
};
export interface ExistingMosipVCItemDetailsProps {
vc: VC;
isBindingPending: boolean;
onBinding?: () => void;
activeTab?: Number;
}
export interface EsignetMosipVCItemDetailsProps {
vc: EsignetVC;
isBindingPending: boolean;
onBinding?: () => void;
activeTab?: number;
}
export interface EsignetVC {
id: string;
idType: VcIdType;
verifiableCredential: VerifiableCredential;
verifiablePresentation?: VerifiablePresentation;
generatedOn: Date;
requestId: string;
isVerified: boolean;
lastVerifiedOn: number;
locked: boolean;
reason?: VCSharingReason[];
shouldVerifyPresence?: boolean;
walletBindingResponse?: WalletBindingResponse;
credentialRegistry: string;
isPinned?: boolean;
hashedId: string;
}
function getFullAddress(credential: CredentialSubject) {
if (!credential) {
return '';
}
const fields = [
'addressLine1',
'addressLine2',
'addressLine3',
'city',
'province',
'region',
];
return fields
.map(field => getLocalizedField(credential[field]))
.concat(credential.postalCode)
.filter(Boolean)
.join(', ');
}