mirror of
https://github.com/mosip/inji-wallet.git
synced 2026-01-08 21:18:14 -05:00
feat: download credentials from Esignet using openId4VCI (#851)
* feat(INJI-245): dowload and view card via issuers Co-authored-by: Harsh Vardhan <harsh59v@gmail.com> * fix(INJI-245): remove vc from wallet Co-authored-by: Harsh Vardhan <harsh59v@gmail.com> * feat(INJI-245): pin card downloaded via eSignet * refactor(INJI-245): remove debug logs * refactor(INJI-245): rename vcItem related component to ExistingVcItem * refactor(INJI-245): add lock file modifications * refactor(INJI-245): add styles in purple theme for issuer related components * refactor(INJI-245): update VID for wallet binding usecase and issuer logo display in vc * refactor(INJI-245): remove duplicate loader component * refactor(INJI-245): remove unused props in vc details container --------- Co-authored-by: Harsh Vardhan <harsh59v@gmail.com> Co-authored-by: Vijay <94220135+vijay151096@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
d71c34c569
commit
55c666b121
3
.env
3
.env
@@ -18,3 +18,6 @@ CREDENTIAL_REGISTRY_EDIT=true
|
||||
|
||||
#supported languages( en, fil, ar, hi, kn, ta)
|
||||
APPLICATION_LANGUAGE=en
|
||||
|
||||
#Toggle for openID for VC
|
||||
ENABLE_OPENID_FOR_VC=false
|
||||
|
||||
@@ -121,7 +121,8 @@ android {
|
||||
|
||||
manifestPlaceholders = [
|
||||
APP_NAME: APP_NAME_RELEASE,
|
||||
GOOGLE_NEARBY_MESSAGES_API_KEY: "${properties.getProperty('GOOGLE_NEARBY_MESSAGES_API_KEY')}"
|
||||
GOOGLE_NEARBY_MESSAGES_API_KEY: "${properties.getProperty('GOOGLE_NEARBY_MESSAGES_API_KEY')}",
|
||||
appAuthRedirectScheme: 'io.mosip.residentapp.inji'
|
||||
]
|
||||
}
|
||||
splits {
|
||||
|
||||
BIN
assets/digit-icon.png
Normal file
BIN
assets/digit-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
BIN
assets/no-internet-connection.png
Normal file
BIN
assets/no-internet-connection.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
BIN
assets/something-went-wrong.png
Normal file
BIN
assets/something-went-wrong.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
@@ -5,7 +5,7 @@ import {WalletBinding} from '../screens/Home/MyVcs/WalletBinding';
|
||||
import {Pressable, View} from 'react-native';
|
||||
import {useKebabPopUp} from './KebabPopUpController';
|
||||
import {ActorRefFrom} from 'xstate';
|
||||
import {vcItemMachine} from '../machines/vcItem';
|
||||
import {ExistingMosipVCItemMachine} from '../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {HistoryTab} from '../screens/Home/MyVcs/HistoryTab';
|
||||
import {RemoveVcWarningOverlay} from '../screens/Home/MyVcs/RemoveVcWarningOverlay';
|
||||
@@ -96,5 +96,5 @@ export interface KebabPopUpProps {
|
||||
vcMetadata: VCMetadata;
|
||||
isVisible: boolean;
|
||||
onDismiss: () => void;
|
||||
service: ActorRefFrom<typeof vcItemMachine>;
|
||||
service: ActorRefFrom<typeof ExistingMosipVCItemMachine>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useSelector } from '@xstate/react';
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import {useSelector} from '@xstate/react';
|
||||
import {ActorRefFrom} from 'xstate';
|
||||
import {
|
||||
selectKebabPopUpWalletBindingInProgress,
|
||||
selectKebabPopUp,
|
||||
@@ -11,49 +11,60 @@ import {
|
||||
selectOtpError,
|
||||
selectShowWalletBindingError,
|
||||
selectWalletBindingError,
|
||||
VcItemEvents,
|
||||
vcItemMachine,
|
||||
ExistingMosipVCItemEvents,
|
||||
ExistingMosipVCItemMachine,
|
||||
selectShowActivities,
|
||||
} from '../machines/vcItem';
|
||||
import { selectActivities } from '../machines/activityLog';
|
||||
import { GlobalContext } from '../shared/GlobalContext';
|
||||
import { useContext } from 'react';
|
||||
import { VCMetadata } from '../shared/VCMetadata';
|
||||
} from '../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine';
|
||||
import {selectActivities} from '../machines/activityLog';
|
||||
import {GlobalContext} from '../shared/GlobalContext';
|
||||
import {useContext} from 'react';
|
||||
import {VCMetadata} from '../shared/VCMetadata';
|
||||
import {
|
||||
EsignetMosipVCItemEvents,
|
||||
EsignetMosipVCItemMachine,
|
||||
} from '../machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine';
|
||||
import {isVCFromOpenId4VCI} from '../shared/openId4VCI/Utils';
|
||||
|
||||
export function useKebabPopUp(props) {
|
||||
const service = props.service as ActorRefFrom<typeof vcItemMachine>;
|
||||
const PIN_CARD = () => service.send(VcItemEvents.PIN_CARD());
|
||||
const KEBAB_POPUP = () => service.send(VcItemEvents.KEBAB_POPUP());
|
||||
const service = props.service as
|
||||
| ActorRefFrom<typeof ExistingMosipVCItemMachine>
|
||||
| ActorRefFrom<typeof EsignetMosipVCItemMachine>;
|
||||
const vcEvents =
|
||||
props.vcKey !== undefined && isVCFromOpenId4VCI(props.vcMetadata)
|
||||
? 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(VcItemEvents.ADD_WALLET_BINDING_ID());
|
||||
const CONFIRM = () => service.send(VcItemEvents.CONFIRM());
|
||||
service.send(vcEvents.ADD_WALLET_BINDING_ID());
|
||||
const CONFIRM = () => service.send(vcEvents.CONFIRM());
|
||||
const REMOVE = (vcMetadata: VCMetadata) =>
|
||||
service.send(VcItemEvents.REMOVE(vcMetadata));
|
||||
const DISMISS = () => service.send(VcItemEvents.DISMISS());
|
||||
const CANCEL = () => service.send(VcItemEvents.CANCEL());
|
||||
const SHOW_ACTIVITY = () => service.send(VcItemEvents.SHOW_ACTIVITY());
|
||||
const INPUT_OTP = (otp: string) => service.send(VcItemEvents.INPUT_OTP(otp));
|
||||
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 isPinned = useSelector(service, selectIsPinned);
|
||||
const isBindingWarning = useSelector(service, selectKebabPopUpBindingWarning);
|
||||
const isRemoveWalletWarning = useSelector(service, selectRemoveWalletWarning);
|
||||
const isAcceptingOtpInput = useSelector(
|
||||
service,
|
||||
selectKebabPopUpAcceptingBindingOtp
|
||||
selectKebabPopUpAcceptingBindingOtp,
|
||||
);
|
||||
const isWalletBindingError = useSelector(
|
||||
service,
|
||||
selectShowWalletBindingError
|
||||
selectShowWalletBindingError,
|
||||
);
|
||||
const otpError = useSelector(service, selectOtpError);
|
||||
const walletBindingError = useSelector(service, selectWalletBindingError);
|
||||
const WalletBindingInProgress = useSelector(
|
||||
service,
|
||||
selectKebabPopUpWalletBindingInProgress
|
||||
selectKebabPopUpWalletBindingInProgress,
|
||||
);
|
||||
const emptyWalletBindingId = useSelector(service, selectEmptyWalletBindingId);
|
||||
const isKebabPopUp = useSelector(service, selectKebabPopUp);
|
||||
const isShowActivities = useSelector(service, selectShowActivities);
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const activityLogService = appService.children.get('activityLog');
|
||||
|
||||
return {
|
||||
|
||||
105
components/VC/EsignetMosipVCItem/EsignetMosipVCItem.tsx
Normal file
105
components/VC/EsignetMosipVCItem/EsignetMosipVCItem.tsx
Normal file
@@ -0,0 +1,105 @@
|
||||
import React, {useContext, useRef} from 'react';
|
||||
import {useInterpret, useSelector} from '@xstate/react';
|
||||
import {Pressable} from 'react-native';
|
||||
import {ActorRefFrom} from 'xstate';
|
||||
import {GlobalContext} from '../../../shared/GlobalContext';
|
||||
import {logState} from '../../../machines/app';
|
||||
import {Theme} from '../../ui/styleUtils';
|
||||
import {Row} from '../../ui';
|
||||
import {KebabPopUp} from '../../KebabPopUp';
|
||||
import {VCMetadata} from '../../../shared/VCMetadata';
|
||||
import {EsignetMosipVCItemContent} from './EsignetMosipVCItemContent';
|
||||
import {EsignetMosipVCActivationStatus} from './EsignetMosipVCItemActivationStatus';
|
||||
import {
|
||||
EsignetMosipVCItemEvents,
|
||||
EsignetMosipVCItemMachine,
|
||||
createEsignetMosipVCItemMachine,
|
||||
selectContext,
|
||||
selectGeneratedOn,
|
||||
selectKebabPopUp,
|
||||
selectVerifiableCredentials,
|
||||
} from '../../../machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine';
|
||||
|
||||
export const EsignetMosipVCItem: React.FC<EsignetMosipVCItemProps> = props => {
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const machine = useRef(
|
||||
createEsignetMosipVCItemMachine(
|
||||
appService.getSnapshot().context.serviceRefs,
|
||||
props.vcMetadata,
|
||||
),
|
||||
);
|
||||
|
||||
const service = useInterpret(machine.current, {devTools: __DEV__});
|
||||
service.subscribe(logState);
|
||||
const context = useSelector(service, selectContext);
|
||||
|
||||
const isKebabPopUp = useSelector(service, selectKebabPopUp);
|
||||
const DISMISS = () => service.send(EsignetMosipVCItemEvents.DISMISS());
|
||||
const KEBAB_POPUP = () =>
|
||||
service.send(EsignetMosipVCItemEvents.KEBAB_POPUP());
|
||||
|
||||
const credentials = useSelector(service, selectVerifiableCredentials);
|
||||
const generatedOn = useSelector(service, selectGeneratedOn);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Pressable
|
||||
onPress={() => props.onPress(service)}
|
||||
disabled={!credentials}
|
||||
style={
|
||||
props.selected
|
||||
? Theme.Styles.selectedBindedVc
|
||||
: Theme.Styles.closeCardBgContainer
|
||||
}>
|
||||
<EsignetMosipVCItemContent
|
||||
context={context}
|
||||
credential={credentials}
|
||||
generatedOn={generatedOn}
|
||||
selectable={props.selectable}
|
||||
selected={props.selected}
|
||||
service={service}
|
||||
iconName={props.iconName}
|
||||
iconType={props.iconType}
|
||||
onPress={() => props.onPress(service)}
|
||||
/>
|
||||
{props.isSharingVc ? null : (
|
||||
<Row crossAlign="center">
|
||||
{props.activeTab !== 'receivedVcsTab' &&
|
||||
props.activeTab != 'sharingVcScreen' && (
|
||||
<EsignetMosipVCActivationStatus
|
||||
verifiableCredential={credentials}
|
||||
showOnlyBindedVc={props.showOnlyBindedVc}
|
||||
emptyWalletBindingId
|
||||
/>
|
||||
)}
|
||||
<Pressable onPress={KEBAB_POPUP}>
|
||||
<KebabPopUp
|
||||
testID="ellipsis"
|
||||
vcMetadata={props.vcMetadata}
|
||||
iconName="dots-three-horizontal"
|
||||
iconType="entypo"
|
||||
isVisible={isKebabPopUp}
|
||||
onDismiss={DISMISS}
|
||||
service={service}
|
||||
/>
|
||||
</Pressable>
|
||||
</Row>
|
||||
)}
|
||||
</Pressable>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export interface EsignetMosipVCItemProps {
|
||||
vcMetadata: VCMetadata;
|
||||
margin?: string;
|
||||
selectable?: boolean;
|
||||
selected?: boolean;
|
||||
showOnlyBindedVc?: boolean;
|
||||
onPress?: (vcRef?: ActorRefFrom<typeof EsignetMosipVCItemMachine>) => void;
|
||||
onShow?: (vcRef?: ActorRefFrom<typeof EsignetMosipVCItemMachine>) => void;
|
||||
activeTab?: string;
|
||||
iconName?: string;
|
||||
iconType?: string;
|
||||
isSharingVc?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
import React from 'react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {Dimensions} from 'react-native';
|
||||
import {Icon} from 'react-native-elements';
|
||||
import {Theme} from '../../ui/styleUtils';
|
||||
import {Row, Text} from '../../ui';
|
||||
import {VerifiableCredential} from './vc';
|
||||
|
||||
const WalletUnverifiedIcon: React.FC = () => {
|
||||
return (
|
||||
<Icon
|
||||
name="shield-alert"
|
||||
color={Theme.Colors.Icon}
|
||||
size={28}
|
||||
type="material-community"
|
||||
containerStyle={{marginStart: 4, bottom: 1}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const WalletVerifiedIcon: React.FC = () => {
|
||||
return (
|
||||
<Icon
|
||||
name="verified-user"
|
||||
color={Theme.Colors.VerifiedIcon}
|
||||
size={28}
|
||||
containerStyle={{marginStart: 4, bottom: 1}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const WalletUnverifiedActivationDetails: React.FC<
|
||||
WalletUnVerifiedDetailsProps
|
||||
> = props => {
|
||||
const {t} = useTranslation('VcDetails');
|
||||
return (
|
||||
<Row
|
||||
width={Dimensions.get('screen').width * 0.8}
|
||||
align="space-between"
|
||||
crossAlign="center">
|
||||
<Row
|
||||
crossAlign="center"
|
||||
style={{
|
||||
flex: 1,
|
||||
}}>
|
||||
{props.verifiableCredential && <WalletUnverifiedIcon />}
|
||||
<Text
|
||||
color={Theme.Colors.Details}
|
||||
testID="activationPending"
|
||||
weight="semibold"
|
||||
size="small"
|
||||
margin="10 33 10 10"
|
||||
style={
|
||||
!props.verifiableCredential
|
||||
? Theme.Styles.loadingTitle
|
||||
: Theme.Styles.statusLabel
|
||||
}>
|
||||
{t('profileAuthenticated')}
|
||||
</Text>
|
||||
</Row>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
const WalletVerifiedActivationDetails: React.FC<
|
||||
WalletVerifiedDetailsProps
|
||||
> = props => {
|
||||
const {t} = useTranslation('VcDetails');
|
||||
return (
|
||||
<Row
|
||||
width={Dimensions.get('screen').width * 0.8}
|
||||
align="space-between"
|
||||
crossAlign="center">
|
||||
<Row
|
||||
crossAlign="center"
|
||||
style={{
|
||||
flex: 1,
|
||||
}}>
|
||||
<WalletVerifiedIcon />
|
||||
<Text
|
||||
color={Theme.Colors.statusLabel}
|
||||
testID="activated"
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
margin="10 10 10 10"
|
||||
style={
|
||||
!props.verifiableCredential
|
||||
? Theme.Styles.loadingTitle
|
||||
: Theme.Styles.subtitle
|
||||
}>
|
||||
{t('profileAuthenticated')}
|
||||
</Text>
|
||||
</Row>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
export const EsignetMosipVCActivationStatus: React.FC<
|
||||
EsignetMosipVCActivationStatusProps
|
||||
> = props => {
|
||||
return (
|
||||
<Row>
|
||||
{props.emptyWalletBindingId ? (
|
||||
<WalletUnverifiedActivationDetails
|
||||
verifiableCredential={props.verifiableCredential}
|
||||
/>
|
||||
) : (
|
||||
<WalletVerifiedActivationDetails
|
||||
verifiableCredential={props.verifiableCredential}
|
||||
showOnlyBindedVc={props.showOnlyBindedVc}
|
||||
/>
|
||||
)}
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
export interface EsignetMosipVCActivationStatusProps {
|
||||
showOnlyBindedVc: boolean;
|
||||
verifiableCredential: VerifiableCredential;
|
||||
emptyWalletBindingId: boolean;
|
||||
}
|
||||
|
||||
interface WalletVerifiedDetailsProps {
|
||||
showOnlyBindedVc: boolean;
|
||||
verifiableCredential: VerifiableCredential;
|
||||
}
|
||||
|
||||
interface WalletUnVerifiedDetailsProps {
|
||||
verifiableCredential: VerifiableCredential;
|
||||
}
|
||||
201
components/VC/EsignetMosipVCItem/EsignetMosipVCItemContent.tsx
Normal file
201
components/VC/EsignetMosipVCItem/EsignetMosipVCItemContent.tsx
Normal file
@@ -0,0 +1,201 @@
|
||||
import React from 'react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {Image, ImageBackground} from 'react-native';
|
||||
import {CheckBox, Icon} from 'react-native-elements';
|
||||
import {Column, Row, Text} from '../../ui';
|
||||
import {Theme} from '../../ui/styleUtils';
|
||||
import VerifiedIcon from '../../VerifiedIcon';
|
||||
import {getLocalizedField} from '../../../i18n';
|
||||
import {Credential, VerifiableCredential} from './vc';
|
||||
import testIDProps from '../../../shared/commonUtil';
|
||||
|
||||
const getDetails = (arg1: string, arg2: string, credential: Credential) => {
|
||||
if (arg1 === 'Status') {
|
||||
return (
|
||||
<Column>
|
||||
<Text
|
||||
testID="status"
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={
|
||||
!credential
|
||||
? Theme.Colors.LoadingDetailsLabel
|
||||
: Theme.Colors.DetailsLabel
|
||||
}>
|
||||
{arg1}
|
||||
</Text>
|
||||
<Row>
|
||||
<Text
|
||||
testID="valid"
|
||||
numLines={1}
|
||||
color={Theme.Colors.Details}
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
style={
|
||||
!credential ? Theme.Styles.loadingTitle : Theme.Styles.subtitle
|
||||
}>
|
||||
{!credential ? '' : arg2}
|
||||
</Text>
|
||||
{!credential ? null : <VerifiedIcon />}
|
||||
</Row>
|
||||
</Column>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Column>
|
||||
<Text
|
||||
color={
|
||||
!credential
|
||||
? Theme.Colors.LoadingDetailsLabel
|
||||
: Theme.Colors.DetailsLabel
|
||||
}
|
||||
size="smaller"
|
||||
weight={'bold'}
|
||||
style={Theme.Styles.vcItemLabelHeader}>
|
||||
{arg1}
|
||||
</Text>
|
||||
<Text
|
||||
numLines={4}
|
||||
color={Theme.Colors.Details}
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
style={
|
||||
!credential ? Theme.Styles.loadingTitle : Theme.Styles.subtitle
|
||||
}>
|
||||
{!credential ? '' : arg2}
|
||||
</Text>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const EsignetMosipVCItemContent: React.FC<
|
||||
EsignetMosipVCItemContentProps
|
||||
> = props => {
|
||||
const fullName = !props.credential
|
||||
? ''
|
||||
: getLocalizedField(
|
||||
props.credential?.credential.credentialSubject?.fullName,
|
||||
);
|
||||
const {t} = useTranslation('VcDetails');
|
||||
const isvalid = !props.credential ? '' : t('valid');
|
||||
const selectableOrCheck = props.selectable ? (
|
||||
<CheckBox
|
||||
checked={props.selected}
|
||||
checkedIcon={<Icon name="radio-button-checked" />}
|
||||
uncheckedIcon={<Icon name="radio-button-unchecked" />}
|
||||
onPress={() => props.onPress()}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<ImageBackground
|
||||
source={!props.credential ? null : Theme.CloseCard}
|
||||
resizeMode="stretch"
|
||||
borderRadius={4}
|
||||
style={
|
||||
!props.credential
|
||||
? Theme.Styles.vertloadingContainer
|
||||
: Theme.Styles.backgroundImageContainer
|
||||
}>
|
||||
<Column>
|
||||
<Row align="space-between">
|
||||
<Row>
|
||||
<ImageBackground
|
||||
source={
|
||||
!props.credential
|
||||
? Theme.ProfileIcon
|
||||
: {uri: props.credential?.credential.credentialSubject.face}
|
||||
}
|
||||
style={Theme.Styles.closeCardImage}>
|
||||
{props.iconName && (
|
||||
<Icon
|
||||
{...testIDProps('pinIcon')}
|
||||
name={props.iconName}
|
||||
type={props.iconType}
|
||||
color={Theme.Colors.Icon}
|
||||
style={{marginLeft: -80}}
|
||||
/>
|
||||
)}
|
||||
</ImageBackground>
|
||||
<Column margin="0 0 0 10">
|
||||
{getDetails(
|
||||
t('fullName'),
|
||||
fullName,
|
||||
props.credential?.credential,
|
||||
)}
|
||||
|
||||
<Column margin="10 0 0 0">
|
||||
<Text
|
||||
color={
|
||||
!props.credential
|
||||
? Theme.Colors.LoadingDetailsLabel
|
||||
: Theme.Colors.DetailsLabel
|
||||
}
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
align="left">
|
||||
{t('idType')}
|
||||
</Text>
|
||||
<Text
|
||||
weight="semibold"
|
||||
color={Theme.Colors.Details}
|
||||
size="smaller"
|
||||
style={
|
||||
!props.credential
|
||||
? Theme.Styles.loadingTitle
|
||||
: Theme.Styles.subtitle
|
||||
}>
|
||||
{t('nationalCard')}
|
||||
</Text>
|
||||
</Column>
|
||||
</Column>
|
||||
</Row>
|
||||
|
||||
<Column>{props.credential ? selectableOrCheck : null}</Column>
|
||||
</Row>
|
||||
|
||||
<Row
|
||||
align="space-between"
|
||||
margin="5 0 0 0"
|
||||
style={!props.credential ? Theme.Styles.loadingContainer : null}>
|
||||
<Column>
|
||||
{!props.credential
|
||||
? getDetails(t('id'), 'newid', props.credential?.credential)
|
||||
: null}
|
||||
{getDetails(
|
||||
t('generatedOn'),
|
||||
props.generatedOn,
|
||||
props.credential?.credential,
|
||||
)}
|
||||
</Column>
|
||||
<Column>
|
||||
{props.credential
|
||||
? getDetails(t('status'), isvalid, props.credential?.credential)
|
||||
: null}
|
||||
</Column>
|
||||
<Column
|
||||
style={{display: props.credential?.credential ? 'flex' : 'none'}}>
|
||||
<Image
|
||||
src={props.credential?.issuerLogo}
|
||||
style={Theme.Styles.issuerLogo}
|
||||
resizeMethod="auto"
|
||||
/>
|
||||
</Column>
|
||||
</Row>
|
||||
</Column>
|
||||
</ImageBackground>
|
||||
);
|
||||
};
|
||||
|
||||
interface EsignetMosipVCItemContentProps {
|
||||
context: any;
|
||||
credential: VerifiableCredential;
|
||||
generatedOn: string;
|
||||
selectable: boolean;
|
||||
selected: boolean;
|
||||
iconName?: string;
|
||||
iconType?: string;
|
||||
service: any;
|
||||
onPress?: () => void;
|
||||
}
|
||||
401
components/VC/EsignetMosipVCItem/EsignetMosipVCItemDetails.tsx
Normal file
401
components/VC/EsignetMosipVCItem/EsignetMosipVCItemDetails.tsx
Normal file
@@ -0,0 +1,401 @@
|
||||
import React from 'react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {Button, Column, Row, Text} from '../../ui';
|
||||
import {Image, ImageBackground, View} from 'react-native';
|
||||
import {Theme} from '../../ui/styleUtils';
|
||||
import {QrCodeOverlay} from '../../QrCodeOverlay';
|
||||
import {getLocalizedField} from '../../../i18n';
|
||||
import VerifiedIcon from '../../VerifiedIcon';
|
||||
import {CREDENTIAL_REGISTRY_EDIT} from 'react-native-dotenv';
|
||||
import {TextItem} from '../../ui/TextItem';
|
||||
import {format, formatDistanceToNow, parse} from 'date-fns';
|
||||
import DateFnsLocale from 'date-fns/locale';
|
||||
import {Icon} from 'react-native-elements';
|
||||
import {WalletBindingResponse} from '../../../shared/cryptoutil/cryptoUtil';
|
||||
import {
|
||||
CredentialSubject,
|
||||
VCSharingReason,
|
||||
VcIdType,
|
||||
VerifiableCredential,
|
||||
VerifiablePresentation,
|
||||
} from './vc';
|
||||
|
||||
export const EsignetMosipVCItemDetails: React.FC<
|
||||
EsignetMosipVCItemDetailsProps
|
||||
> = props => {
|
||||
const {t, i18n} = useTranslation('VcDetails');
|
||||
|
||||
if (props.vc?.verifiableCredential == null) {
|
||||
return <Text align="center">Loading details...</Text>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Column margin="10">
|
||||
<ImageBackground
|
||||
borderRadius={10}
|
||||
style={Theme.Styles.openCardBgContainer}
|
||||
source={Theme.OpenCard}>
|
||||
<Row align="space-between">
|
||||
<Column align="space-evenly" crossAlign="center">
|
||||
<Image
|
||||
source={
|
||||
props.vc?.verifiableCredential.credential.credentialSubject
|
||||
?.face
|
||||
? {
|
||||
uri: props.vc?.verifiableCredential.credential
|
||||
.credentialSubject.face,
|
||||
}
|
||||
: Theme.ProfileIcon
|
||||
}
|
||||
style={Theme.Styles.openCardImage}
|
||||
/>
|
||||
|
||||
<QrCodeOverlay
|
||||
qrCodeDetailes={String(props.vc.verifiableCredential)}
|
||||
/>
|
||||
<Column margin="20 0 0 0">
|
||||
<Image
|
||||
src={props.vc.verifiableCredential.issuerLogo}
|
||||
style={Theme.Styles.issuerLogo}
|
||||
/>
|
||||
</Column>
|
||||
</Column>
|
||||
<Column align="space-evenly">
|
||||
<Column>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('fullName')}
|
||||
</Text>
|
||||
<Text
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{getLocalizedField(
|
||||
props.vc?.verifiableCredential.credential.credentialSubject
|
||||
.fullName,
|
||||
)}
|
||||
</Text>
|
||||
</Column>
|
||||
<Row>
|
||||
<Column>
|
||||
<Column>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('idType')}
|
||||
</Text>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{t('nationalCard')}
|
||||
</Text>
|
||||
</Column>
|
||||
{props.vc?.verifiableCredential.credential.credentialSubject
|
||||
.VID ? (
|
||||
<Column margin="20 0 0 0">
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('vid')}
|
||||
</Text>
|
||||
<Text
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{
|
||||
props.vc?.verifiableCredential.credential
|
||||
.credentialSubject.VID
|
||||
}
|
||||
</Text>
|
||||
</Column>
|
||||
) : null}
|
||||
<Column margin="20 0 0 0">
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('dateOfBirth')}
|
||||
</Text>
|
||||
<Text
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{format(
|
||||
parse(
|
||||
getLocalizedField(
|
||||
props.vc?.verifiableCredential.credential
|
||||
.credentialSubject.dateOfBirth,
|
||||
),
|
||||
'yyyy/MM/dd',
|
||||
new Date(),
|
||||
),
|
||||
'yyy/MM/dd',
|
||||
)}
|
||||
</Text>
|
||||
</Column>
|
||||
</Column>
|
||||
<Column margin="0 0 0 40">
|
||||
<Column>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('gender')}
|
||||
</Text>
|
||||
<Text
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{getLocalizedField(
|
||||
props.vc?.verifiableCredential.credential
|
||||
.credentialSubject.gender,
|
||||
)}
|
||||
</Text>
|
||||
</Column>
|
||||
<Column margin="20 0 0 0">
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('generatedOn')}
|
||||
</Text>
|
||||
<Text
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{new Date(props.vc?.generatedOn).toLocaleDateString()}
|
||||
</Text>
|
||||
</Column>
|
||||
<Column margin="20 0 0 0">
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('status')}
|
||||
</Text>
|
||||
<Row>
|
||||
<Text
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{t('valid')}
|
||||
</Text>
|
||||
{props.vc?.isVerified && <VerifiedIcon />}
|
||||
</Row>
|
||||
</Column>
|
||||
<Column margin="20 0 0 0">
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('phoneNumber')}
|
||||
</Text>
|
||||
<Text
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{getLocalizedField(
|
||||
props.vc?.verifiableCredential.credential
|
||||
.credentialSubject.phone,
|
||||
)}
|
||||
</Text>
|
||||
</Column>
|
||||
</Column>
|
||||
</Row>
|
||||
</Column>
|
||||
</Row>
|
||||
<View style={Theme.Styles.hrLine}></View>
|
||||
<Column>
|
||||
<Column fill style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('email')}
|
||||
</Text>
|
||||
<Row>
|
||||
<Text
|
||||
style={
|
||||
props.vc?.verifiableCredential.credential.credentialSubject
|
||||
.email.length > 25
|
||||
? {flex: 1}
|
||||
: {flex: 0}
|
||||
}
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{getLocalizedField(
|
||||
props.vc?.verifiableCredential.credential.credentialSubject
|
||||
.email,
|
||||
)}
|
||||
</Text>
|
||||
</Row>
|
||||
</Column>
|
||||
|
||||
<Column style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('address')}
|
||||
</Text>
|
||||
<Row>
|
||||
<Text
|
||||
style={{flex: 1}}
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{getFullAddress(
|
||||
props.vc?.verifiableCredential.credential.credentialSubject,
|
||||
)}
|
||||
</Text>
|
||||
</Row>
|
||||
</Column>
|
||||
{CREDENTIAL_REGISTRY_EDIT === 'true' && (
|
||||
<Column fill style={Theme.Styles.labelPart}>
|
||||
<Text
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.DetailsLabel}>
|
||||
{t('credentialRegistry')}
|
||||
</Text>
|
||||
<Text
|
||||
weight="semibold"
|
||||
size="smaller"
|
||||
color={Theme.Colors.Details}>
|
||||
{props.vc?.credentialRegistry}
|
||||
</Text>
|
||||
</Column>
|
||||
)}
|
||||
</Column>
|
||||
</ImageBackground>
|
||||
|
||||
{props.vc?.reason?.length > 0 && (
|
||||
<Text margin="24 24 16 24" weight="semibold">
|
||||
{t('reasonForSharing')}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{props.vc?.reason?.map((reason, index) => (
|
||||
<TextItem
|
||||
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}>
|
||||
<Row margin={'0 0 5 0'} crossAlign={'center'}>
|
||||
<Icon
|
||||
name="shield-alert"
|
||||
color={Theme.Colors.Icon}
|
||||
size={30}
|
||||
type="material-community"
|
||||
/>
|
||||
<Text
|
||||
style={{flex: 1}}
|
||||
weight="semibold"
|
||||
size="small"
|
||||
margin={'0 0 5 0'}
|
||||
color={Theme.Colors.statusLabel}>
|
||||
{t('offlineAuthDisabledHeader')}
|
||||
</Text>
|
||||
</Row>
|
||||
<Text
|
||||
style={{flex: 1}}
|
||||
weight="regular"
|
||||
size="small"
|
||||
margin={'0 0 5 0'}
|
||||
color={Theme.Colors.statusLabel}>
|
||||
{t('offlineAuthDisabledMessage')}
|
||||
</Text>
|
||||
|
||||
<Button
|
||||
title={t('enableVerification')}
|
||||
onPress={props.onBinding}
|
||||
type="radius"
|
||||
/>
|
||||
</Column>
|
||||
) : (
|
||||
<Column style={Theme.Styles.openCardBgContainer}>
|
||||
<Row crossAlign="center">
|
||||
<Icon
|
||||
name="verified-user"
|
||||
color={Theme.Colors.VerifiedIcon}
|
||||
size={28}
|
||||
containerStyle={{marginStart: 4, bottom: 1}}
|
||||
/>
|
||||
<Text
|
||||
numLines={1}
|
||||
color={Theme.Colors.statusLabel}
|
||||
weight="bold"
|
||||
size="smaller"
|
||||
margin="10 10 10 10"
|
||||
children={t('profileAuthenticated')}></Text>
|
||||
</Row>
|
||||
</Column>
|
||||
)
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
export interface EsignetMosipVCItemDetailsProps {
|
||||
vc: VC;
|
||||
isBindingPending: boolean;
|
||||
onBinding?: () => void;
|
||||
activeTab?: number;
|
||||
}
|
||||
|
||||
export interface VC {
|
||||
id: string;
|
||||
idType: VcIdType;
|
||||
tag: string;
|
||||
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(', ');
|
||||
}
|
||||
117
components/VC/EsignetMosipVCItem/vc.ts
Normal file
117
components/VC/EsignetMosipVCItem/vc.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import {WalletBindingResponse} from '../../../shared/cryptoutil/cryptoUtil';
|
||||
|
||||
export interface VC {
|
||||
id?: string;
|
||||
idType?: VcIdType;
|
||||
credential?: DecodedCredential;
|
||||
verifiableCredential: VerifiableCredential;
|
||||
verifiablePresentation?: VerifiablePresentation;
|
||||
requestId?: string;
|
||||
isVerified?: boolean;
|
||||
lastVerifiedOn: number;
|
||||
reason?: VCSharingReason[];
|
||||
shouldVerifyPresence?: boolean;
|
||||
walletBindingResponse?: WalletBindingResponse;
|
||||
credentialRegistry?: string;
|
||||
isPinned?: boolean;
|
||||
hashedId?: string;
|
||||
}
|
||||
|
||||
export interface VCSharingReason {
|
||||
timestamp: number;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export type VcIdType = 'UIN' | 'VID';
|
||||
|
||||
export interface DecodedCredential {
|
||||
biometrics: {
|
||||
face: string;
|
||||
finger: {
|
||||
left_thumb: string;
|
||||
right_thumb: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface CredentialSubject {
|
||||
//TODO: This should change to mandatory field if uin is also issued
|
||||
UIN?: string;
|
||||
VID?: string;
|
||||
addressLine1: LocalizedField[] | string;
|
||||
city: LocalizedField[] | string;
|
||||
dateOfBirth: string;
|
||||
email: string;
|
||||
fullName: LocalizedField[] | string;
|
||||
gender: LocalizedField[] | string;
|
||||
id: string;
|
||||
phone: string;
|
||||
face: string;
|
||||
}
|
||||
|
||||
type VCContext = (string | Record<string, unknown>)[];
|
||||
|
||||
export interface Credential {
|
||||
'@context': VCContext;
|
||||
credentialSubject: CredentialSubject;
|
||||
id: string;
|
||||
issuanceDate: string;
|
||||
issuer: string;
|
||||
proof: {
|
||||
created: string;
|
||||
jws: string;
|
||||
proofPurpose: 'assertionMethod' | string;
|
||||
type: 'RsaSignature2018' | string;
|
||||
verificationMethod: string;
|
||||
};
|
||||
type: VerifiableCredentialType[];
|
||||
}
|
||||
|
||||
export interface VerifiableCredential {
|
||||
format: string;
|
||||
credential: Credential;
|
||||
identifier: string;
|
||||
generatedOn: Date;
|
||||
issuerLogo: string;
|
||||
}
|
||||
|
||||
export interface VerifiablePresentation {
|
||||
'@context': VCContext;
|
||||
verifiableCredential: VerifiableCredential[];
|
||||
type: 'VerifiablePresentation';
|
||||
proof: {
|
||||
created: string;
|
||||
jws: string;
|
||||
proofPurpose: 'authentication' | string;
|
||||
type: 'RsaSignature2018' | string;
|
||||
verificationMethod: string;
|
||||
challenge: string;
|
||||
domain: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type VerifiableCredentialType =
|
||||
| 'VerifiableCredential'
|
||||
| 'MOSIPVerfiableCredential'
|
||||
| string;
|
||||
|
||||
export interface VCLabel {
|
||||
singular: string;
|
||||
plural: string;
|
||||
}
|
||||
|
||||
export interface LocalizedField {
|
||||
language: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface linkTransactionResponse {
|
||||
authFactors: Object[];
|
||||
authorizeScopes: null;
|
||||
clientName: string;
|
||||
configs: {};
|
||||
essentialClaims: string[];
|
||||
linkTransactionId: string;
|
||||
logoUrl: string;
|
||||
voluntaryClaims: string[];
|
||||
}
|
||||
@@ -3,32 +3,34 @@ import {useInterpret, useSelector} from '@xstate/react';
|
||||
import {View, Pressable} from 'react-native';
|
||||
import {ActorRefFrom} from 'xstate';
|
||||
import {
|
||||
createVcItemMachine,
|
||||
createExistingMosipVCItemMachine,
|
||||
selectVerifiableCredential,
|
||||
selectGeneratedOn,
|
||||
vcItemMachine,
|
||||
ExistingMosipVCItemMachine,
|
||||
selectContext,
|
||||
selectTag,
|
||||
selectEmptyWalletBindingId,
|
||||
selectIsSavingFailedInIdle,
|
||||
selectKebabPopUp,
|
||||
} from '../machines/vcItem';
|
||||
import {VcItemEvents} from '../machines/vcItem';
|
||||
import {ErrorMessageOverlay} from './MessageOverlay';
|
||||
import {Theme} from './ui/styleUtils';
|
||||
import {GlobalContext} from '../shared/GlobalContext';
|
||||
import {VcItemContent} from './VcItemContent';
|
||||
import {VcItemActivationStatus} from './VcItemActivationStatus';
|
||||
import {Row} from './ui';
|
||||
import {KebabPopUp} from './KebabPopUp';
|
||||
import {logState} from '../machines/app';
|
||||
import {VCMetadata} from '../shared/VCMetadata';
|
||||
} from '../../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine';
|
||||
import {ExistingMosipVCItemEvents} from '../../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine';
|
||||
import {ErrorMessageOverlay} from '../../MessageOverlay';
|
||||
import {Theme} from '../../ui/styleUtils';
|
||||
import {GlobalContext} from '../../../shared/GlobalContext';
|
||||
import {ExistingMosipVCItemContent} from './ExistingMosipVCItemContent';
|
||||
import {ExistingMosipVCItemActivationStatus} from './ExistingMosipVCItemActivationStatus';
|
||||
import {Row} from '../../ui';
|
||||
import {KebabPopUp} from '../../KebabPopUp';
|
||||
import {logState} from '../../../machines/app';
|
||||
import {VCMetadata} from '../../../shared/VCMetadata';
|
||||
import {format} from 'date-fns';
|
||||
|
||||
export const VcItem: React.FC<VcItemProps> = props => {
|
||||
export const ExistingMosipVCItem: React.FC<
|
||||
ExistingMosipVCItemProps
|
||||
> = props => {
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const machine = useRef(
|
||||
createVcItemMachine(
|
||||
createExistingMosipVCItemMachine(
|
||||
appService.getSnapshot().context.serviceRefs,
|
||||
props.vcMetadata,
|
||||
),
|
||||
@@ -44,8 +46,9 @@ export const VcItem: React.FC<VcItemProps> = props => {
|
||||
const verifiableCredential = useSelector(service, selectVerifiableCredential);
|
||||
const emptyWalletBindingId = useSelector(service, selectEmptyWalletBindingId);
|
||||
const isKebabPopUp = useSelector(service, selectKebabPopUp);
|
||||
const DISMISS = () => service.send(VcItemEvents.DISMISS());
|
||||
const KEBAB_POPUP = () => service.send(VcItemEvents.KEBAB_POPUP());
|
||||
const DISMISS = () => service.send(ExistingMosipVCItemEvents.DISMISS());
|
||||
const KEBAB_POPUP = () =>
|
||||
service.send(ExistingMosipVCItemEvents.KEBAB_POPUP());
|
||||
const isSavingFailedInIdle = useSelector(service, selectIsSavingFailedInIdle);
|
||||
|
||||
const storeErrorTranslationPath = 'errors.savingFailed';
|
||||
@@ -64,7 +67,7 @@ export const VcItem: React.FC<VcItemProps> = props => {
|
||||
? Theme.Styles.selectedBindedVc
|
||||
: Theme.Styles.closeCardBgContainer
|
||||
}>
|
||||
<VcItemContent
|
||||
<ExistingMosipVCItemContent
|
||||
context={context}
|
||||
verifiableCredential={verifiableCredential}
|
||||
generatedOn={formattedDate}
|
||||
@@ -81,7 +84,7 @@ export const VcItem: React.FC<VcItemProps> = props => {
|
||||
<Row style={Theme.Styles.activationTab}>
|
||||
{props.activeTab !== 'receivedVcsTab' &&
|
||||
props.activeTab != 'sharingVcScreen' && (
|
||||
<VcItemActivationStatus
|
||||
<ExistingMosipVCItemActivationStatus
|
||||
verifiableCredential={verifiableCredential}
|
||||
emptyWalletBindingId={emptyWalletBindingId}
|
||||
onPress={() => props.onPress(service)}
|
||||
@@ -114,14 +117,14 @@ export const VcItem: React.FC<VcItemProps> = props => {
|
||||
);
|
||||
};
|
||||
|
||||
interface VcItemProps {
|
||||
export interface ExistingMosipVCItemProps {
|
||||
vcMetadata: VCMetadata;
|
||||
margin?: string;
|
||||
selectable?: boolean;
|
||||
selected?: boolean;
|
||||
showOnlyBindedVc?: boolean;
|
||||
onPress?: (vcRef?: ActorRefFrom<typeof vcItemMachine>) => void;
|
||||
onShow?: (vcRef?: ActorRefFrom<typeof vcItemMachine>) => void;
|
||||
onPress?: (vcRef?: ActorRefFrom<typeof ExistingMosipVCItemMachine>) => void;
|
||||
onShow?: (vcRef?: ActorRefFrom<typeof ExistingMosipVCItemMachine>) => void;
|
||||
activeTab?: string;
|
||||
iconName?: string;
|
||||
iconType?: string;
|
||||
@@ -3,10 +3,10 @@ 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';
|
||||
import {ExistingMosipVCItemMachine} from '../../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine';
|
||||
import {VerifiableCredential} from '../../../types/vc';
|
||||
import {Row, Text} from '../../ui';
|
||||
import {Theme} from '../../ui/styleUtils';
|
||||
|
||||
const WalletUnverifiedIcon: React.FC = () => {
|
||||
return (
|
||||
@@ -94,8 +94,8 @@ const WalletVerifiedActivationDetails: React.FC<
|
||||
);
|
||||
};
|
||||
|
||||
export const VcItemActivationStatus: React.FC<
|
||||
VcItemActivationStatusProps
|
||||
export const ExistingMosipVCItemActivationStatus: React.FC<
|
||||
ExistingMosipVCItemActivationStatusProps
|
||||
> = props => {
|
||||
return (
|
||||
<Row margin="0 0 0 -6">
|
||||
@@ -115,20 +115,20 @@ export const VcItemActivationStatus: React.FC<
|
||||
);
|
||||
};
|
||||
|
||||
interface VcItemActivationStatusProps {
|
||||
interface ExistingMosipVCItemActivationStatusProps {
|
||||
showOnlyBindedVc: boolean;
|
||||
onPress: (vcRef?: ActorRefFrom<typeof vcItemMachine>) => void;
|
||||
onPress: (vcRef?: ActorRefFrom<typeof ExistingMosipVCItemMachine>) => void;
|
||||
verifiableCredential: VerifiableCredential;
|
||||
emptyWalletBindingId: boolean;
|
||||
}
|
||||
|
||||
interface WalletVerifiedDetailsProps {
|
||||
showOnlyBindedVc: boolean;
|
||||
onPress: (vcRef?: ActorRefFrom<typeof vcItemMachine>) => void;
|
||||
onPress: (vcRef?: ActorRefFrom<typeof ExistingMosipVCItemMachine>) => void;
|
||||
verifiableCredential: VerifiableCredential;
|
||||
}
|
||||
|
||||
interface WalletUnVerifiedDetailsProps {
|
||||
onPress: (vcRef?: ActorRefFrom<typeof vcItemMachine>) => void;
|
||||
onPress: (vcRef?: ActorRefFrom<typeof ExistingMosipVCItemMachine>) => void;
|
||||
verifiableCredential: VerifiableCredential;
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
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 {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';
|
||||
import testIDProps from '../shared/commonUtil';
|
||||
import testIDProps from '../../../shared/commonUtil';
|
||||
|
||||
const getDetails = (arg1, arg2, verifiableCredential) => {
|
||||
if (arg1 === 'Status') {
|
||||
@@ -88,7 +88,9 @@ function getIdNumber(id: string) {
|
||||
return '*'.repeat(id.length - 4) + id.slice(-4);
|
||||
}
|
||||
|
||||
export const VcItemContent: React.FC<VcItemContentProps> = props => {
|
||||
export const ExistingMosipVCItemContent: React.FC<
|
||||
ExistingMosipVCItemContentProps
|
||||
> = 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;
|
||||
@@ -293,7 +295,7 @@ export const VcItemContent: React.FC<VcItemContentProps> = props => {
|
||||
);
|
||||
};
|
||||
|
||||
interface VcItemContentProps {
|
||||
interface ExistingMosipVCItemContentProps {
|
||||
context: any;
|
||||
verifiableCredential: VerifiableCredential;
|
||||
generatedOn: string;
|
||||
@@ -4,17 +4,19 @@ 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';
|
||||
import {Button, Column, Row, Text} from './ui';
|
||||
import {Theme} from './ui/styleUtils';
|
||||
import {TextItem} from './ui/TextItem';
|
||||
import {VcItemTags} from './VcItemTags';
|
||||
import VerifiedIcon from './VerifiedIcon';
|
||||
import {getLocalizedField} from '../i18n';
|
||||
import {VC, CredentialSubject} from '../../../types/vc';
|
||||
import {Button, Column, Row, Text} from '../../ui';
|
||||
import {Theme} from '../../ui/styleUtils';
|
||||
import {TextItem} from '../../ui/TextItem';
|
||||
import {VcItemTags} from '../../VcItemTags';
|
||||
import VerifiedIcon from '../../VerifiedIcon';
|
||||
import {getLocalizedField} from '../../../i18n';
|
||||
import {CREDENTIAL_REGISTRY_EDIT} from 'react-native-dotenv';
|
||||
import {QrCodeOverlay} from './QrCodeOverlay';
|
||||
import {QrCodeOverlay} from '../../QrCodeOverlay';
|
||||
|
||||
export const VcDetails: React.FC<VcDetailsProps> = props => {
|
||||
export const ExistingMosipVCItemDetails: React.FC<
|
||||
ExistingMosipVCItemDetailsProps
|
||||
> = props => {
|
||||
const {t, i18n} = useTranslation('VcDetails');
|
||||
|
||||
//Assigning the UIN and VID from the VC details to display the idtype label
|
||||
@@ -382,7 +384,7 @@ export const VcDetails: React.FC<VcDetailsProps> = props => {
|
||||
);
|
||||
};
|
||||
|
||||
interface VcDetailsProps {
|
||||
export interface ExistingMosipVCItemDetailsProps {
|
||||
vc: VC;
|
||||
isBindingPending: boolean;
|
||||
onBinding?: () => void;
|
||||
19
components/VC/VcDetailsContainer.tsx
Normal file
19
components/VC/VcDetailsContainer.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
EsignetMosipVCItemDetails,
|
||||
EsignetMosipVCItemDetailsProps,
|
||||
} from './EsignetMosipVCItem/EsignetMosipVCItemDetails';
|
||||
import {VCMetadata} from '../../shared/VCMetadata';
|
||||
import {
|
||||
ExistingMosipVCItemDetails,
|
||||
ExistingMosipVCItemDetailsProps,
|
||||
} from './ExistingMosipVCItem/ExistingMosipVCItemDetails';
|
||||
|
||||
export const VcDetailsContainer: React.FC<
|
||||
EsignetMosipVCItemDetailsProps | ExistingMosipVCItemDetailsProps
|
||||
> = props => {
|
||||
if (VCMetadata.fromVC(props.vc.vcMetadata).isFromOpenId4VCI()) {
|
||||
return <EsignetMosipVCItemDetails {...props} />;
|
||||
}
|
||||
return <ExistingMosipVCItemDetails {...props} />;
|
||||
};
|
||||
18
components/VC/VcItemContainer.tsx
Normal file
18
components/VC/VcItemContainer.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import {
|
||||
EsignetMosipVCItem,
|
||||
EsignetMosipVCItemProps,
|
||||
} from './EsignetMosipVCItem/EsignetMosipVCItem';
|
||||
import React from 'react';
|
||||
import {
|
||||
ExistingMosipVCItem,
|
||||
ExistingMosipVCItemProps,
|
||||
} from './ExistingMosipVCItem/ExistingMosipVCItem';
|
||||
|
||||
export const VcItemContainer: React.FC<
|
||||
ExistingMosipVCItemProps | EsignetMosipVCItemProps
|
||||
> = props => {
|
||||
if (props.vcMetadata.isFromOpenId4VCI()) {
|
||||
return <EsignetMosipVCItem {...props} />;
|
||||
}
|
||||
return <ExistingMosipVCItem {...props} />;
|
||||
};
|
||||
@@ -1,30 +1,30 @@
|
||||
import React, { useContext, useRef } from 'react';
|
||||
import { useInterpret, useSelector } from '@xstate/react';
|
||||
import { Pressable } from 'react-native';
|
||||
import { CheckBox } from 'react-native-elements';
|
||||
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 {ActorRefFrom} from 'xstate';
|
||||
import {
|
||||
createVcItemMachine,
|
||||
createExistingMosipVCItemMachine,
|
||||
selectVerifiableCredential,
|
||||
selectGeneratedOn,
|
||||
selectId,
|
||||
vcItemMachine,
|
||||
} from '../machines/vcItem';
|
||||
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';
|
||||
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<VcItemProps> = (props) => {
|
||||
const { appService } = useContext(GlobalContext);
|
||||
export const VidItem: React.FC<VcItemProps> = props => {
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const machine = useRef(
|
||||
createVcItemMachine(
|
||||
createExistingMosipVCItemMachine(
|
||||
appService.getSnapshot().context.serviceRefs,
|
||||
props.vcMetadata
|
||||
)
|
||||
props.vcMetadata,
|
||||
),
|
||||
);
|
||||
const service = useInterpret(machine.current);
|
||||
const uin = useSelector(service, selectId);
|
||||
@@ -83,7 +83,7 @@ export const VidItem: React.FC<VcItemProps> = (props) => {
|
||||
{!verifiableCredential
|
||||
? ''
|
||||
: getLocalizedField(
|
||||
verifiableCredential.credentialSubject.fullName
|
||||
verifiableCredential.credentialSubject.fullName,
|
||||
) +
|
||||
' · ' +
|
||||
generatedOn}
|
||||
@@ -104,5 +104,5 @@ interface VcItemProps {
|
||||
margin?: string;
|
||||
selectable?: boolean;
|
||||
selected?: boolean;
|
||||
onPress?: (vcRef?: ActorRefFrom<typeof vcItemMachine>) => void;
|
||||
onPress?: (vcRef?: ActorRefFrom<typeof ExistingMosipVCItemMachine>) => void;
|
||||
}
|
||||
|
||||
66
components/openId4VCI/Issuer.tsx
Normal file
66
components/openId4VCI/Issuer.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import React from 'react';
|
||||
import {Image, Pressable} from 'react-native';
|
||||
import {Theme} from '../ui/styleUtils';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import testIDProps from '../../shared/commonUtil';
|
||||
import {Text} from '../ui/Text';
|
||||
|
||||
function isValidURL(urlString: string) {
|
||||
const urlPattern = new RegExp(
|
||||
`^(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3}))(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*(\\?[;&a-z\\d%_.~+=-]*)?(\\#[-a-z\\d_]*)?$`,
|
||||
'i',
|
||||
);
|
||||
return !!urlPattern.test(urlString);
|
||||
}
|
||||
|
||||
export const Issuer: React.FC<IssuerProps> = (props: IssuerProps) => {
|
||||
/**
|
||||
* This check is added since the logo for Donwload via UIN/VID is present in the repo where as
|
||||
* other issuers has the logo url specfied in its data itself
|
||||
*/
|
||||
|
||||
const {t} = useTranslation('IssuersScreen');
|
||||
function getSource() {
|
||||
if (isValidURL(props.logoUrl)) return {uri: props.logoUrl};
|
||||
return props.logoUrl;
|
||||
}
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
{...testIDProps(props.testID)}
|
||||
onPress={props.onPress}
|
||||
style={({pressed}) =>
|
||||
pressed
|
||||
? [
|
||||
Theme.issuersScreenStyles.issuerBoxContainerPressed,
|
||||
Theme.Styles.boxShadow,
|
||||
]
|
||||
: [
|
||||
Theme.issuersScreenStyles.issuerBoxContainer,
|
||||
Theme.Styles.boxShadow,
|
||||
]
|
||||
}>
|
||||
<Image
|
||||
{...testIDProps('issuerIcon')}
|
||||
style={Theme.issuersScreenStyles.issuerIcon}
|
||||
source={getSource()}
|
||||
/>
|
||||
<Text testID="heading" style={Theme.issuersScreenStyles.issuerHeading}>
|
||||
{t('itemHeading', {issuer: props.displayName})}
|
||||
</Text>
|
||||
<Text
|
||||
testID="description"
|
||||
style={Theme.issuersScreenStyles.issuerDescription}>
|
||||
{t('itemSubHeading')}
|
||||
</Text>
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
|
||||
interface IssuerProps {
|
||||
id: string;
|
||||
displayName: string;
|
||||
logoUrl: string;
|
||||
onPress: () => void;
|
||||
testID: string;
|
||||
}
|
||||
@@ -9,7 +9,8 @@ import {Theme, Spacing} from './styleUtils';
|
||||
import testIDProps from '../../shared/commonUtil';
|
||||
|
||||
export const Button: React.FC<ButtonProps> = props => {
|
||||
const type = props.type || 'solid' || 'radius' || 'gradient';
|
||||
const type =
|
||||
props.type || 'solid' || 'radius' || 'gradient' || 'clearAddIdBtnBg';
|
||||
const buttonStyle: StyleProp<ViewStyle> = [
|
||||
props.fill ? Theme.ButtonStyles.fill : null,
|
||||
Theme.ButtonStyles[type],
|
||||
|
||||
75
components/ui/Error.tsx
Normal file
75
components/ui/Error.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import {useFocusEffect} from '@react-navigation/native';
|
||||
import React from 'react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {BackHandler, Dimensions, View} from 'react-native';
|
||||
import {Button, Column, Row, Text} from '.';
|
||||
import {Header} from './Header';
|
||||
import {Theme} from './styleUtils';
|
||||
import testIDProps from '../../shared/commonUtil';
|
||||
|
||||
export const Error: React.FC<ErrorProps> = props => {
|
||||
const {t} = useTranslation('common');
|
||||
|
||||
useFocusEffect(
|
||||
React.useCallback(() => {
|
||||
const onBackPress = () => {
|
||||
props.goBack();
|
||||
return true;
|
||||
};
|
||||
|
||||
const disableBackHandler = BackHandler.addEventListener(
|
||||
'hardwareBackPress',
|
||||
onBackPress,
|
||||
);
|
||||
|
||||
return () => disableBackHandler.remove();
|
||||
}, []),
|
||||
);
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
...Theme.ModalStyles.modal,
|
||||
backgroundColor: Theme.Colors.whiteBackgroundColor,
|
||||
}}
|
||||
{...testIDProps(props.testID)}>
|
||||
<Column fill safe>
|
||||
{props.goBack && <Header testID="errorHeader" goBack={props.goBack} />}
|
||||
<Column fill safe align="space-evenly">
|
||||
<View style={{alignItems: 'center'}}>
|
||||
<View>
|
||||
<Row align="center" style={Theme.ErrorStyles.image}>
|
||||
{props.image}
|
||||
</Row>
|
||||
<Text style={Theme.ErrorStyles.title} testID="errorTitle">
|
||||
{props.title}
|
||||
</Text>
|
||||
<Text style={Theme.ErrorStyles.message} testID="errorMessage">
|
||||
{props.message}
|
||||
</Text>
|
||||
</View>
|
||||
{props.tryAgain && (
|
||||
<Button
|
||||
onPress={props.tryAgain}
|
||||
width={Dimensions.get('screen').width * 0.46}
|
||||
title={t('tryAgain')}
|
||||
type="outline"
|
||||
testID="tryAgain"
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</Column>
|
||||
</Column>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export interface ErrorProps {
|
||||
isVisible: boolean;
|
||||
title: string;
|
||||
message: string;
|
||||
image: React.ReactElement;
|
||||
goBack: () => void;
|
||||
tryAgain: null | (() => void);
|
||||
testID: string;
|
||||
}
|
||||
54
components/ui/Header.tsx
Normal file
54
components/ui/Header.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import React from 'react';
|
||||
import {Text, TouchableOpacity, View} from 'react-native';
|
||||
import {Icon} from 'react-native-elements';
|
||||
import {Column, Row} from './Layout';
|
||||
import {Theme} from './styleUtils';
|
||||
import testIDProps from '../../shared/commonUtil';
|
||||
|
||||
export const Header: React.FC<HeaderProps> = ({goBack, title, testID}) => {
|
||||
return (
|
||||
<Column safe align="center" testID={testID}>
|
||||
<Row elevation={2}>
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginTop: 18,
|
||||
marginBottom: 22,
|
||||
marginVertical: 16,
|
||||
}}>
|
||||
<TouchableOpacity onPress={goBack} {...testIDProps('goBack')}>
|
||||
<Icon
|
||||
name="arrow-left"
|
||||
type="material-community"
|
||||
onPress={goBack}
|
||||
containerStyle={{
|
||||
...Theme.Styles.backArrowContainer,
|
||||
marginLeft: 10,
|
||||
}}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<Row fill align={'center'}>
|
||||
<Column>
|
||||
<View style={{alignItems: 'center', marginLeft: -40}}>
|
||||
<Text
|
||||
style={Theme.TextStyles.semiBoldHeader}
|
||||
{...testIDProps('title')}>
|
||||
{title}
|
||||
</Text>
|
||||
</View>
|
||||
</Column>
|
||||
</Row>
|
||||
</View>
|
||||
</Row>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
interface HeaderProps {
|
||||
title?: string;
|
||||
goBack: () => void;
|
||||
testID: string;
|
||||
}
|
||||
86
components/ui/Loader.tsx
Normal file
86
components/ui/Loader.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
import React, {Fragment} from 'react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {Image, SafeAreaView, View} from 'react-native';
|
||||
import Spinner from 'react-native-spinkit';
|
||||
import {Button, Centered, Column, Row, Text} from '../../components/ui';
|
||||
import {Theme} from '../../components/ui/styleUtils';
|
||||
import testIDProps from '../../shared/commonUtil';
|
||||
|
||||
export const Loader: React.FC<LoaderProps> = props => {
|
||||
const {t} = useTranslation('ScanScreen');
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Row elevation={3}>
|
||||
<SafeAreaView style={Theme.ModalStyles.header}>
|
||||
<Row
|
||||
fill
|
||||
align={'flex-start'}
|
||||
style={Theme.LoaderStyles.titleContainer}>
|
||||
<View style={Theme.issuersScreenStyles.loaderHeadingText}>
|
||||
<Text style={Theme.TextStyles.header} testID="loaderTitle">
|
||||
{props.title}
|
||||
</Text>
|
||||
{props.subTitle && (
|
||||
<Text
|
||||
style={Theme.TextStyles.subHeader}
|
||||
color={Theme.Colors.profileValue}
|
||||
testID="loaderSubTitle">
|
||||
{props.subTitle}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
</Row>
|
||||
</SafeAreaView>
|
||||
</Row>
|
||||
<Centered crossAlign="center" fill>
|
||||
<Column margin="24 0" align="space-around">
|
||||
<Image
|
||||
source={Theme.InjiProgressingLogo}
|
||||
height={2}
|
||||
width={2}
|
||||
style={{marginLeft: -6}}
|
||||
{...testIDProps('progressingLogo')}
|
||||
/>
|
||||
<View {...testIDProps('threeDotsLoader')}>
|
||||
<Spinner
|
||||
type="ThreeBounce"
|
||||
color={Theme.Colors.Loading}
|
||||
style={{marginLeft: 6}}
|
||||
/>
|
||||
</View>
|
||||
</Column>
|
||||
|
||||
<Column style={{display: props.hint ? 'flex' : 'none'}}>
|
||||
<Column style={Theme.SelectVcOverlayStyles.timeoutHintContainer}>
|
||||
<Text
|
||||
align="center"
|
||||
color={Theme.Colors.TimoutText}
|
||||
style={Theme.TextStyles.bold}>
|
||||
{props.hint}
|
||||
</Text>
|
||||
{props.onCancel && (
|
||||
<Button
|
||||
type="clear"
|
||||
title={t('common:cancel')}
|
||||
onPress={props.onCancel}
|
||||
/>
|
||||
)}
|
||||
</Column>
|
||||
</Column>
|
||||
</Centered>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export interface LoaderProps {
|
||||
isVisible: boolean;
|
||||
title?: string;
|
||||
subTitle?: string;
|
||||
label?: string;
|
||||
hint?: string;
|
||||
onCancel?: () => void;
|
||||
requester?: boolean;
|
||||
progress?: boolean | number;
|
||||
onBackdropPress?: () => void;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
import {Dimensions, StyleSheet, ViewStyle} from 'react-native';
|
||||
import {Dimensions, Platform, StyleSheet, ViewStyle} from 'react-native';
|
||||
import {Spacing} from '../styleUtils';
|
||||
|
||||
const Colors = {
|
||||
@@ -17,6 +17,7 @@ const Colors = {
|
||||
Orange: '#F2811D',
|
||||
LightGrey: '#F7F7F7',
|
||||
ShadeOfGrey: '#6F6F6F',
|
||||
mediumDarkGrey: '#7B7B7B',
|
||||
White: '#FFFFFF',
|
||||
Red: '#D52929',
|
||||
Green: '#4B9D20',
|
||||
@@ -88,6 +89,7 @@ export const DefaultTheme = {
|
||||
DefaultToggle: Colors.LightOrange,
|
||||
ProfileIconBg: Colors.LightOrange,
|
||||
GrayText: Colors.GrayText,
|
||||
errorGrayText: Colors.mediumDarkGrey,
|
||||
gradientBtn: ['#F59B4B', '#E86E04'],
|
||||
dotColor: Colors.dorColor,
|
||||
plainText: Colors.plainText,
|
||||
@@ -324,6 +326,11 @@ export const DefaultTheme = {
|
||||
width: 40,
|
||||
marginRight: 4,
|
||||
},
|
||||
issuerLogo: {
|
||||
resizeMode: 'contain',
|
||||
aspectRatio: 1,
|
||||
height: 60,
|
||||
},
|
||||
vcDetailsLogo: {
|
||||
height: 35,
|
||||
width: 90,
|
||||
@@ -495,6 +502,20 @@ export const DefaultTheme = {
|
||||
marginLeft: 10,
|
||||
marginRight: 10,
|
||||
},
|
||||
downloadFabIcon: {
|
||||
height: 70,
|
||||
width: 70,
|
||||
borderRadius: 200,
|
||||
padding: 10,
|
||||
backgroundColor: Colors.Orange,
|
||||
shadowColor: '#000',
|
||||
shadowOpacity: 0.4,
|
||||
elevation: 5,
|
||||
position: 'absolute',
|
||||
bottom: Dimensions.get('window').width * 0.1,
|
||||
right: Dimensions.get('window').width * 0.1,
|
||||
},
|
||||
boxShadow: generateBoxShadowStyle(),
|
||||
}),
|
||||
QrCodeStyles: StyleSheet.create({
|
||||
magnifierZoom: {
|
||||
@@ -570,6 +591,19 @@ export const DefaultTheme = {
|
||||
lineHeight: 19,
|
||||
paddingTop: 4,
|
||||
},
|
||||
subHeader: {
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
lineHeight: 19,
|
||||
fontSize: 15,
|
||||
paddingTop: 10,
|
||||
},
|
||||
semiBoldHeader: {
|
||||
color: Colors.Black,
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
fontSize: 18,
|
||||
lineHeight: 21,
|
||||
paddingTop: 4,
|
||||
},
|
||||
retrieveIdLabel: {
|
||||
color: Colors.ShadeOfGrey,
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
@@ -600,6 +634,12 @@ export const DefaultTheme = {
|
||||
fontFamily: 'Inter_400Regular',
|
||||
fontSize: 14,
|
||||
},
|
||||
regularGrey: {
|
||||
fontFamily: 'Inter_400Regular',
|
||||
fontSize: 15,
|
||||
lineHeight: 19,
|
||||
color: Colors.ShadeOfGrey,
|
||||
},
|
||||
semibold: {
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
fontSize: 15,
|
||||
@@ -660,6 +700,12 @@ export const DefaultTheme = {
|
||||
fontSize: 12,
|
||||
},
|
||||
}),
|
||||
LoaderStyles: StyleSheet.create({
|
||||
titleContainer: {
|
||||
marginLeft: Dimensions.get('screen').width * 0.08,
|
||||
marginVertical: Dimensions.get('screen').height * 0.025,
|
||||
},
|
||||
}),
|
||||
ButtonStyles: StyleSheet.create({
|
||||
fill: {
|
||||
flex: 1,
|
||||
@@ -778,6 +824,13 @@ export const DefaultTheme = {
|
||||
width: Dimensions.get('screen').width,
|
||||
height: Dimensions.get('screen').height,
|
||||
},
|
||||
header: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'flex-start',
|
||||
marginHorizontal: 18,
|
||||
marginVertical: 8,
|
||||
},
|
||||
}),
|
||||
UpdateModalStyles: StyleSheet.create({
|
||||
modal: {
|
||||
@@ -1039,6 +1092,85 @@ export const DefaultTheme = {
|
||||
backgroundColor: Colors.Transparent,
|
||||
},
|
||||
}),
|
||||
issuersScreenStyles: StyleSheet.create({
|
||||
issuerListOuterContainer: {
|
||||
padding: 10,
|
||||
flex: 1,
|
||||
backgroundColor: Colors.White,
|
||||
},
|
||||
issuersContainer: {marginHorizontal: 3},
|
||||
issuerBoxContainer: {
|
||||
margin: 5,
|
||||
flex: 1,
|
||||
padding: 10,
|
||||
borderRadius: 6,
|
||||
alignItems: 'flex-start',
|
||||
justifyContent: 'space-evenly',
|
||||
flexDirection: 'column',
|
||||
paddingHorizontal: 6,
|
||||
paddingVertical: 8,
|
||||
backgroundColor: Colors.White,
|
||||
},
|
||||
issuerBoxContainerPressed: {
|
||||
margin: 5,
|
||||
flex: 1,
|
||||
padding: 10,
|
||||
borderRadius: 6,
|
||||
alignItems: 'flex-start',
|
||||
justifyContent: 'space-evenly',
|
||||
flexDirection: 'column',
|
||||
paddingHorizontal: 6,
|
||||
paddingVertical: 8,
|
||||
backgroundColor: Colors.Grey,
|
||||
},
|
||||
issuerHeading: {
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
fontSize: 14,
|
||||
lineHeight: 17,
|
||||
paddingHorizontal: 3,
|
||||
paddingBottom: 4,
|
||||
},
|
||||
issuerDescription: {
|
||||
fontSize: 11,
|
||||
lineHeight: 14,
|
||||
color: Colors.ShadeOfGrey,
|
||||
paddingVertical: 5,
|
||||
paddingHorizontal: 3,
|
||||
},
|
||||
issuerIcon: {
|
||||
resizeMode: 'contain',
|
||||
height: 33,
|
||||
width: 32,
|
||||
marginBottom: 9,
|
||||
marginTop: 8,
|
||||
marginLeft: 2.5,
|
||||
},
|
||||
loaderHeadingText: {
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
},
|
||||
}),
|
||||
ErrorStyles: StyleSheet.create({
|
||||
image: {marginTop: -60, paddingBottom: 26},
|
||||
title: {
|
||||
color: Colors.Black,
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
fontSize: 18,
|
||||
lineHeight: 21,
|
||||
paddingTop: 4,
|
||||
textAlign: 'center',
|
||||
},
|
||||
message: {
|
||||
textAlign: 'center',
|
||||
fontFamily: 'Inter_400Regular',
|
||||
fontSize: 14,
|
||||
lineHeight: 20,
|
||||
marginTop: 6,
|
||||
marginBottom: 25,
|
||||
marginHorizontal: 40,
|
||||
color: Colors.mediumDarkGrey,
|
||||
},
|
||||
}),
|
||||
|
||||
ICON_SMALL_SIZE: 16,
|
||||
ICON_MID_SIZE: 22,
|
||||
@@ -1070,6 +1202,9 @@ export const DefaultTheme = {
|
||||
IntroScanner: require('../../../assets/intro-scanner.png'),
|
||||
injiSmallLogo: require('../../../assets/inji_small_logo.png'),
|
||||
protectPrivacy: require('../../../assets/phone_mockup_1.png'),
|
||||
NoInternetConnection: require('../../../assets/no-internet-connection.png'),
|
||||
SomethingWentWrong: require('../../../assets/something-went-wrong.png'),
|
||||
DigitIcon: require('../../../assets/digit-icon.png'),
|
||||
|
||||
elevation(level: ElevationLevel): ViewStyle {
|
||||
// https://ethercreative.github.io/react-native-shadow-generator/
|
||||
@@ -1113,3 +1248,19 @@ export const DefaultTheme = {
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
function generateBoxShadowStyle() {
|
||||
if (Platform.OS === 'ios') {
|
||||
return {
|
||||
shadowColor: '#000',
|
||||
shadowOffset: {width: 1, height: 1.2},
|
||||
shadowOpacity: 0.3,
|
||||
shadowRadius: 2.5,
|
||||
};
|
||||
} else if (Platform.OS === 'android') {
|
||||
return {
|
||||
elevation: 4,
|
||||
shadowColor: '#000',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
import {Dimensions, StyleSheet, ViewStyle} from 'react-native';
|
||||
import {Dimensions, Platform, StyleSheet, ViewStyle} from 'react-native';
|
||||
import {Spacing} from '../styleUtils';
|
||||
|
||||
const Colors = {
|
||||
@@ -18,6 +18,7 @@ const Colors = {
|
||||
LightOrange: '#FDF1E6',
|
||||
LightGrey: '#FAF9FF',
|
||||
ShadeOfGrey: '#6F6F6F',
|
||||
mediumDarkGrey: '#7B7B7B',
|
||||
White: '#FFFFFF',
|
||||
Red: '#EB5757',
|
||||
Green: '#219653',
|
||||
@@ -58,6 +59,7 @@ export const PurpleTheme = {
|
||||
borderBottomColor: Colors.Grey6,
|
||||
whiteBackgroundColor: Colors.White,
|
||||
lightGreyBackgroundColor: Colors.LightGrey,
|
||||
errorGrayText: Colors.mediumDarkGrey,
|
||||
profileLanguageValue: Colors.Grey,
|
||||
aboutVersion: Colors.Gray40,
|
||||
profileAuthFactorUnlock: Colors.Grey,
|
||||
@@ -325,6 +327,11 @@ export const PurpleTheme = {
|
||||
width: 40,
|
||||
marginRight: 4,
|
||||
},
|
||||
issuerLogo: {
|
||||
resizeMode: 'contain',
|
||||
aspectRatio: 1,
|
||||
height: 60,
|
||||
},
|
||||
vcDetailsLogo: {
|
||||
height: 35,
|
||||
width: 90,
|
||||
@@ -496,6 +503,20 @@ export const PurpleTheme = {
|
||||
marginLeft: 10,
|
||||
marginRight: 10,
|
||||
},
|
||||
downloadFabIcon: {
|
||||
height: 70,
|
||||
width: 70,
|
||||
borderRadius: 200,
|
||||
padding: 10,
|
||||
backgroundColor: Colors.Purple,
|
||||
shadowColor: '#000',
|
||||
shadowOpacity: 0.4,
|
||||
elevation: 5,
|
||||
position: 'absolute',
|
||||
bottom: Dimensions.get('window').width * 0.1,
|
||||
right: Dimensions.get('window').width * 0.1,
|
||||
},
|
||||
boxShadow: generateBoxShadowStyle(),
|
||||
}),
|
||||
QrCodeStyles: StyleSheet.create({
|
||||
magnifierZoom: {
|
||||
@@ -571,6 +592,19 @@ export const PurpleTheme = {
|
||||
lineHeight: 19,
|
||||
paddingTop: 4,
|
||||
},
|
||||
subHeader: {
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
lineHeight: 19,
|
||||
fontSize: 15,
|
||||
paddingTop: 10,
|
||||
},
|
||||
semiBoldHeader: {
|
||||
color: Colors.Black,
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
fontSize: 18,
|
||||
lineHeight: 21,
|
||||
paddingTop: 4,
|
||||
},
|
||||
retrieveIdLabel: {
|
||||
color: Colors.ShadeOfGrey,
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
@@ -601,6 +635,12 @@ export const PurpleTheme = {
|
||||
fontFamily: 'Inter_400Regular',
|
||||
fontSize: 14,
|
||||
},
|
||||
regularGrey: {
|
||||
fontFamily: 'Inter_400Regular',
|
||||
fontSize: 15,
|
||||
lineHeight: 19,
|
||||
color: Colors.ShadeOfGrey,
|
||||
},
|
||||
semibold: {
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
fontSize: 15,
|
||||
@@ -661,6 +701,12 @@ export const PurpleTheme = {
|
||||
fontSize: 12,
|
||||
},
|
||||
}),
|
||||
LoaderStyles: StyleSheet.create({
|
||||
titleContainer: {
|
||||
marginLeft: Dimensions.get('screen').width * 0.08,
|
||||
marginVertical: Dimensions.get('screen').height * 0.025,
|
||||
},
|
||||
}),
|
||||
ButtonStyles: StyleSheet.create({
|
||||
fill: {
|
||||
flex: 1,
|
||||
@@ -779,6 +825,13 @@ export const PurpleTheme = {
|
||||
width: Dimensions.get('screen').width,
|
||||
height: Dimensions.get('screen').height,
|
||||
},
|
||||
header: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'flex-start',
|
||||
marginHorizontal: 18,
|
||||
marginVertical: 8,
|
||||
},
|
||||
}),
|
||||
UpdateModalStyles: StyleSheet.create({
|
||||
modal: {
|
||||
@@ -1040,6 +1093,81 @@ export const PurpleTheme = {
|
||||
backgroundColor: Colors.Transparent,
|
||||
},
|
||||
}),
|
||||
issuersScreenStyles: StyleSheet.create({
|
||||
issuerListOuterContainer: {
|
||||
padding: 10,
|
||||
flex: 1,
|
||||
backgroundColor: Colors.White,
|
||||
},
|
||||
issuersContainer: {marginHorizontal: 3},
|
||||
issuerBoxContainer: {
|
||||
margin: 5,
|
||||
flex: 1,
|
||||
padding: 10,
|
||||
borderRadius: 6,
|
||||
alignItems: 'flex-start',
|
||||
justifyContent: 'space-evenly',
|
||||
flexDirection: 'column',
|
||||
paddingHorizontal: 6,
|
||||
paddingVertical: 8,
|
||||
backgroundColor: Colors.White,
|
||||
},
|
||||
issuerBoxContainerPressed: {
|
||||
margin: 5,
|
||||
flex: 1,
|
||||
padding: 10,
|
||||
borderRadius: 6,
|
||||
alignItems: 'flex-start',
|
||||
justifyContent: 'space-evenly',
|
||||
flexDirection: 'column',
|
||||
paddingHorizontal: 6,
|
||||
paddingVertical: 8,
|
||||
backgroundColor: Colors.Grey,
|
||||
},
|
||||
issuerHeading: {
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
fontSize: 14,
|
||||
lineHeight: 17,
|
||||
},
|
||||
issuerDescription: {
|
||||
fontSize: 11,
|
||||
lineHeight: 14,
|
||||
color: Colors.ShadeOfGrey,
|
||||
},
|
||||
issuerIcon: {
|
||||
resizeMode: 'contain',
|
||||
height: 33,
|
||||
width: 32,
|
||||
marginBottom: 9,
|
||||
marginTop: 8,
|
||||
marginLeft: 2.5,
|
||||
},
|
||||
loaderHeadingText: {
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
},
|
||||
}),
|
||||
ErrorStyles: StyleSheet.create({
|
||||
image: {marginTop: -60, paddingBottom: 26},
|
||||
title: {
|
||||
color: Colors.Black,
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
fontSize: 18,
|
||||
lineHeight: 21,
|
||||
paddingTop: 4,
|
||||
textAlign: 'center',
|
||||
},
|
||||
message: {
|
||||
textAlign: 'center',
|
||||
fontFamily: 'Inter_400Regular',
|
||||
fontSize: 14,
|
||||
lineHeight: 20,
|
||||
marginTop: 6,
|
||||
marginBottom: 25,
|
||||
marginHorizontal: 40,
|
||||
color: Colors.mediumDarkGrey,
|
||||
},
|
||||
}),
|
||||
ICON_SMALL_SIZE: 16,
|
||||
ICON_MID_SIZE: 22,
|
||||
PinIcon: require('../../../assets/pin_icon.png'),
|
||||
@@ -1070,6 +1198,9 @@ export const PurpleTheme = {
|
||||
IntroScanner: require('../../../assets/intro-scanner.png'),
|
||||
injiSmallLogo: require('../../../assets/inji_small_logo.png'),
|
||||
protectPrivacy: require('../../../assets/phone_mockup_1.png'),
|
||||
NoInternetConnection: require('../../../assets/no-internet-connection.png'),
|
||||
SomethingWentWrong: require('../../../assets/something-went-wrong.png'),
|
||||
DigitIcon: require('../../../assets/digit-icon.png'),
|
||||
|
||||
elevation(level: ElevationLevel): ViewStyle {
|
||||
// https://ethercreative.github.io/react-native-shadow-generator/
|
||||
@@ -1113,3 +1244,19 @@ export const PurpleTheme = {
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
function generateBoxShadowStyle() {
|
||||
if (Platform.OS === 'ios') {
|
||||
return {
|
||||
shadowColor: '#000',
|
||||
shadowOffset: {width: 1, height: 1.2},
|
||||
shadowOpacity: 0.3,
|
||||
shadowRadius: 2.5,
|
||||
};
|
||||
} else if (Platform.OS === 'android') {
|
||||
return {
|
||||
elevation: 4,
|
||||
shadowColor: '#000',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
#import <RCTAppDelegate.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <React/RCTBridgeDelegate.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "RNAppAuthAuthorizationFlowManager.h"
|
||||
|
||||
#import <Expo/Expo.h>
|
||||
|
||||
@interface AppDelegate : EXAppDelegateWrapper
|
||||
@interface AppDelegate : EXAppDelegateWrapper <UIApplicationDelegate, RCTBridgeDelegate, RNAppAuthAuthorizationFlowManager>
|
||||
|
||||
@end
|
||||
@property(nonatomic, weak)id<RNAppAuthAuthorizationFlowManagerDelegate>authorizationFlowManagerDelegate;
|
||||
|
||||
@end
|
||||
@@ -1,4 +1,10 @@
|
||||
PODS:
|
||||
- AppAuth (1.6.2):
|
||||
- AppAuth/Core (= 1.6.2)
|
||||
- AppAuth/ExternalUserAgent (= 1.6.2)
|
||||
- AppAuth/Core (1.6.2)
|
||||
- AppAuth/ExternalUserAgent (1.6.2):
|
||||
- AppAuth/Core
|
||||
- ASN1Decoder (1.8.0)
|
||||
- boost (1.76.0)
|
||||
- BVLinearGradient (2.8.2):
|
||||
@@ -345,6 +351,9 @@ PODS:
|
||||
- React-jsinspector (0.71.8)
|
||||
- React-logger (0.71.8):
|
||||
- glog
|
||||
- react-native-app-auth (7.0.0):
|
||||
- AppAuth (~> 1.6)
|
||||
- React-Core
|
||||
- react-native-location (2.5.0):
|
||||
- React
|
||||
- react-native-mmkv-storage (0.9.1):
|
||||
@@ -366,6 +375,8 @@ PODS:
|
||||
- React-Core
|
||||
- react-native-secure-keystore (0.1.1):
|
||||
- React-Core
|
||||
- react-native-spinkit (1.4.1):
|
||||
- React
|
||||
- react-native-tuvali (0.4.4):
|
||||
- CrcSwift (~> 0.0.3)
|
||||
- GzipSwift
|
||||
@@ -471,6 +482,8 @@ PODS:
|
||||
- React-Core
|
||||
- RNKeychain (8.0.0):
|
||||
- React-Core
|
||||
- RNLocalize (3.0.2):
|
||||
- React-Core
|
||||
- RNPermissions (3.8.4):
|
||||
- React-Core
|
||||
- RNScreens (3.20.0):
|
||||
@@ -538,6 +551,7 @@ DEPENDENCIES:
|
||||
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
|
||||
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
|
||||
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
|
||||
- react-native-app-auth (from `../node_modules/react-native-app-auth`)
|
||||
- react-native-location (from `../node_modules/react-native-location`)
|
||||
- react-native-mmkv-storage (from `../node_modules/react-native-mmkv-storage`)
|
||||
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
|
||||
@@ -546,6 +560,7 @@ DEPENDENCIES:
|
||||
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
|
||||
- react-native-secure-key-store (from `../node_modules/react-native-secure-key-store`)
|
||||
- react-native-secure-keystore (from `../node_modules/react-native-secure-keystore`)
|
||||
- react-native-spinkit (from `../node_modules/react-native-spinkit`)
|
||||
- react-native-tuvali (from `../node_modules/react-native-tuvali`)
|
||||
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
|
||||
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
|
||||
@@ -568,6 +583,7 @@ DEPENDENCIES:
|
||||
- RNFS (from `../node_modules/react-native-fs`)
|
||||
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
|
||||
- RNKeychain (from `../node_modules/react-native-keychain`)
|
||||
- RNLocalize (from `../node_modules/react-native-localize`)
|
||||
- RNPermissions (from `../node_modules/react-native-permissions`)
|
||||
- RNScreens (from `../node_modules/react-native-screens`)
|
||||
- RNSecureRandom (from `../node_modules/react-native-securerandom`)
|
||||
@@ -577,6 +593,7 @@ DEPENDENCIES:
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- AppAuth
|
||||
- ASN1Decoder
|
||||
- CatCrypto
|
||||
- CrcSwift
|
||||
@@ -683,6 +700,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native/ReactCommon/jsinspector"
|
||||
React-logger:
|
||||
:path: "../node_modules/react-native/ReactCommon/logger"
|
||||
react-native-app-auth:
|
||||
:path: "../node_modules/react-native-app-auth"
|
||||
react-native-location:
|
||||
:path: "../node_modules/react-native-location"
|
||||
react-native-mmkv-storage:
|
||||
@@ -699,6 +718,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native-secure-key-store"
|
||||
react-native-secure-keystore:
|
||||
:path: "../node_modules/react-native-secure-keystore"
|
||||
react-native-spinkit:
|
||||
:path: "../node_modules/react-native-spinkit"
|
||||
react-native-tuvali:
|
||||
:path: "../node_modules/react-native-tuvali"
|
||||
React-perflogger:
|
||||
@@ -743,6 +764,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native-gesture-handler"
|
||||
RNKeychain:
|
||||
:path: "../node_modules/react-native-keychain"
|
||||
RNLocalize:
|
||||
:path: "../node_modules/react-native-localize"
|
||||
RNPermissions:
|
||||
:path: "../node_modules/react-native-permissions"
|
||||
RNScreens:
|
||||
@@ -757,12 +780,13 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native/ReactCommon/yoga"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
AppAuth: 3bb1d1cd9340bd09f5ed189fb00b1cc28e1e8570
|
||||
ASN1Decoder: 6110fdeacfdb41559b1481457a1645be716610aa
|
||||
boost: 57d2868c099736d80fcd648bf211b4431e51a558
|
||||
BVLinearGradient: 916632041121a658c704df89d99f04acb038de0f
|
||||
CatCrypto: a477899b6be4954e75be4897e732da098cc0a5a8
|
||||
CrcSwift: f85dea6b41dddb5f98bb3743fd777ce58b77bc2e
|
||||
DoubleConversion: cf9b38bf0b2d048436d9a82ad2abe1404f11e7de
|
||||
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
|
||||
EASClient: 950674e1098ebc09c4c2cf064a61e42e84d9d4c6
|
||||
EXApplication: d8f53a7eee90a870a75656280e8d4b85726ea903
|
||||
EXBarCodeScanner: 8e23fae8d267dbef9f04817833a494200f1fce35
|
||||
@@ -785,7 +809,7 @@ SPEC CHECKSUMS:
|
||||
FBLazyVector: f637f31eacba90d4fdeff3fa41608b8f361c173b
|
||||
FBReactNativeSpec: 0d9a4f4de7ab614c49e98c00aedfd3bfbda33d59
|
||||
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
|
||||
glog: 73c2498ac6884b13ede40eda8228cb1eee9d9d62
|
||||
glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b
|
||||
GoogleInterchangeUtilities: d5bc4d88d5b661ab72f9d70c58d02ca8c27ad1f7
|
||||
GoogleNetworkingUtilities: 3edd3a8161347494f2da60ea0deddc8a472d94cb
|
||||
GoogleSymbolUtilities: 631ee17048aa5e9ab133470d768ea997a5ef9b96
|
||||
@@ -815,6 +839,7 @@ SPEC CHECKSUMS:
|
||||
React-jsiexecutor: 747911ab5921641b4ed7e4900065896597142125
|
||||
React-jsinspector: c712f9e3bb9ba4122d6b82b4f906448b8a281580
|
||||
React-logger: 342f358b8decfbf8f272367f4eacf4b6154061be
|
||||
react-native-app-auth: 1d12b6874a24152715a381d8e9149398ce7c2c95
|
||||
react-native-location: 5a40ec1cc6abf2f6d94df979f98ec76c3a415681
|
||||
react-native-mmkv-storage: cfb6854594cfdc5f7383a9e464bb025417d1721c
|
||||
react-native-netinfo: 2517ad504b3d303e90d7a431b0fcaef76d207983
|
||||
@@ -823,6 +848,7 @@ SPEC CHECKSUMS:
|
||||
react-native-safe-area-context: 39c2d8be3328df5d437ac1700f4f3a4f75716acc
|
||||
react-native-secure-key-store: 910e6df6bc33cb790aba6ee24bc7818df1fe5898
|
||||
react-native-secure-keystore: d9e791a495d23c33db7711732f3a761533b882db
|
||||
react-native-spinkit: da294fd828216ad211fe36a5c14c1e09f09e62db
|
||||
react-native-tuvali: 6c4660b1d72c8d049ea8f6470cc67aa22c89b40d
|
||||
React-perflogger: d21f182895de9d1b077f8a3cd00011095c8c9100
|
||||
React-RCTActionSheet: 0151f83ef92d2a7139bba7dfdbc8066632a6d47b
|
||||
@@ -845,6 +871,7 @@ SPEC CHECKSUMS:
|
||||
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
|
||||
RNGestureHandler: 071d7a9ad81e8b83fe7663b303d132406a7d8f39
|
||||
RNKeychain: 4f63aada75ebafd26f4bc2c670199461eab85d94
|
||||
RNLocalize: dbea38dcb344bf80ff18a1757b1becf11f70cae4
|
||||
RNPermissions: f1b49dd05fa9b83993cd05a9ee115247944d8f1a
|
||||
RNScreens: 218801c16a2782546d30bd2026bb625c0302d70f
|
||||
RNSecureRandom: 07efbdf2cd99efe13497433668e54acd7df49fef
|
||||
@@ -855,4 +882,4 @@ SPEC CHECKSUMS:
|
||||
|
||||
PODFILE CHECKSUM: 01f58b130fa221dabb14b2d82d981ef24dcaba53
|
||||
|
||||
COCOAPODS: 1.11.3
|
||||
COCOAPODS: 1.12.1
|
||||
|
||||
@@ -157,6 +157,34 @@
|
||||
"version": "Version",
|
||||
"tuvaliVersion": "Tuvali-version"
|
||||
},
|
||||
"IssuersScreen": {
|
||||
"title": "Add new card",
|
||||
"header": "Please select a preferred method from below to add a new card",
|
||||
"itemHeading":"Download via {{issuer}}",
|
||||
"itemSubHeading": "Enter the mentioned ID and get your card",
|
||||
"modal": {
|
||||
"title": "In Progress",
|
||||
"hint": "downloading your credential from issuer"
|
||||
},
|
||||
"loaders": {
|
||||
"loading": "Loading...",
|
||||
"subTitle": {
|
||||
"displayIssuers": "Fetching Issuers",
|
||||
"settingUp": "Setting up",
|
||||
"downloadingCredentials": "Downloading Credentials"
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"noInternetConnection": {
|
||||
"title": "No internet connection",
|
||||
"message": "Please connect with internet and retry."
|
||||
},
|
||||
"generic": {
|
||||
"title": "Something went wrong!",
|
||||
"message": "Our experts are working hard to make things working again."
|
||||
}
|
||||
}
|
||||
},
|
||||
"HelpScreen": {
|
||||
"header": "Help",
|
||||
"whatIsDigitalCredential?": "What is a digital credential?",
|
||||
|
||||
@@ -0,0 +1,782 @@
|
||||
import {assign, ErrorPlatformEvent, EventFrom, send, StateFrom} from 'xstate';
|
||||
import {createModel} from 'xstate/lib/model';
|
||||
import {AppServices} from '../../../shared/GlobalContext';
|
||||
import {VCMetadata} from '../../../shared/VCMetadata';
|
||||
import {VC} from '../../../types/vc';
|
||||
import {
|
||||
generateKeys,
|
||||
isCustomSecureKeystore,
|
||||
WalletBindingResponse,
|
||||
} from '../../../shared/cryptoutil/cryptoUtil';
|
||||
import {log} from 'xstate/lib/actions';
|
||||
import {OpenId4VCIProtocol} from '../../../shared/openId4VCI/Utils';
|
||||
import {StoreEvents} from '../../../machines/store';
|
||||
import {MIMOTO_BASE_URL, MY_VCS_STORE_KEY} from '../../../shared/constants';
|
||||
import {VcEvents} from '../../../machines/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 'react-native-secure-keystore';
|
||||
import {VerifiableCredential} from './vc';
|
||||
|
||||
const model = createModel(
|
||||
{
|
||||
serviceRefs: {} as AppServices,
|
||||
vcMetadata: {} as VCMetadata,
|
||||
generatedOn: new Date() as Date,
|
||||
verifiableCredential: null as VerifiableCredential,
|
||||
isPinned: false,
|
||||
hashedId: '',
|
||||
|
||||
publicKey: '',
|
||||
privateKey: '',
|
||||
myVcs: [] as string[],
|
||||
otp: '',
|
||||
otpError: '',
|
||||
idError: '',
|
||||
transactionId: '',
|
||||
bindingTransactionId: '',
|
||||
walletBindingResponse: null as WalletBindingResponse,
|
||||
walletBindingError: '',
|
||||
},
|
||||
{
|
||||
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: () => ({}),
|
||||
LOCK_VC: () => ({}),
|
||||
INPUT_OTP: (otp: string) => ({otp}),
|
||||
REFRESH: () => ({}),
|
||||
REVOKE_VC: () => ({}),
|
||||
ADD_WALLET_BINDING_ID: () => ({}),
|
||||
CANCEL: () => ({}),
|
||||
CONFIRM: () => ({}),
|
||||
STORE_ERROR: (error: Error) => ({error}),
|
||||
PIN_CARD: () => ({}),
|
||||
KEBAB_POPUP: () => ({}),
|
||||
SHOW_ACTIVITY: () => ({}),
|
||||
REMOVE: (vcMetadata: VCMetadata) => ({vcMetadata}),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
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<typeof model>,
|
||||
},
|
||||
on: {
|
||||
REFRESH: {
|
||||
target: '.checkingStore',
|
||||
},
|
||||
},
|
||||
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', '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', 'setGeneratedOn', 'updateVc'],
|
||||
target: 'idle',
|
||||
},
|
||||
},
|
||||
},
|
||||
showBindingWarning: {
|
||||
on: {
|
||||
CONFIRM: {
|
||||
target: 'requestingBindingOtp',
|
||||
},
|
||||
CANCEL: {
|
||||
target: 'idle',
|
||||
},
|
||||
},
|
||||
},
|
||||
requestingBindingOtp: {
|
||||
invoke: {
|
||||
src: 'requestBindingOtp',
|
||||
onDone: [
|
||||
{
|
||||
target: 'acceptingBindingOtp',
|
||||
},
|
||||
],
|
||||
onError: [
|
||||
{
|
||||
actions: ['setWalletBindingError', 'logWalletBindingFailure'],
|
||||
target: 'showingWalletBindingError',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
showingWalletBindingError: {
|
||||
on: {
|
||||
CANCEL: {
|
||||
target: 'idle',
|
||||
actions: 'setWalletBindingErrorEmpty',
|
||||
},
|
||||
},
|
||||
},
|
||||
acceptingBindingOtp: {
|
||||
entry: ['clearOtp'],
|
||||
on: {
|
||||
INPUT_OTP: {
|
||||
target: 'addKeyPair',
|
||||
actions: ['setOtp'],
|
||||
},
|
||||
DISMISS: {
|
||||
target: 'idle',
|
||||
actions: ['clearOtp', 'clearTransactionId'],
|
||||
},
|
||||
},
|
||||
},
|
||||
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: 'idle',
|
||||
actions: [
|
||||
'setWalletBindingId',
|
||||
'setThumbprintForWalletBindingId',
|
||||
'storeContext',
|
||||
'updateVc',
|
||||
'setWalletBindingErrorEmpty',
|
||||
'logWalletBindingSuccess',
|
||||
],
|
||||
},
|
||||
{
|
||||
target: 'updatingPrivateKey',
|
||||
actions: [
|
||||
'setWalletBindingId',
|
||||
'setThumbprintForWalletBindingId',
|
||||
],
|
||||
},
|
||||
],
|
||||
onError: [
|
||||
{
|
||||
actions: ['setWalletBindingError', 'logWalletBindingFailure'],
|
||||
target: 'showingWalletBindingError',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
updatingPrivateKey: {
|
||||
invoke: {
|
||||
src: 'updatePrivateKey',
|
||||
onDone: {
|
||||
actions: [
|
||||
'storeContext',
|
||||
'updatePrivateKey',
|
||||
'updateVc',
|
||||
'setWalletBindingErrorEmpty',
|
||||
'logWalletBindingSuccess',
|
||||
],
|
||||
target: 'idle',
|
||||
},
|
||||
onError: {
|
||||
actions: ['setWalletBindingError', 'logWalletBindingFailure'],
|
||||
target: 'showingWalletBindingError',
|
||||
},
|
||||
},
|
||||
},
|
||||
idle: {
|
||||
on: {
|
||||
DISMISS: {
|
||||
target: 'checkingVc',
|
||||
},
|
||||
KEBAB_POPUP: {
|
||||
target: 'kebabPopUp',
|
||||
},
|
||||
ADD_WALLET_BINDING_ID: {
|
||||
target: 'showBindingWarning',
|
||||
},
|
||||
PIN_CARD: {
|
||||
target: 'pinCard',
|
||||
actions: 'setPinCard',
|
||||
},
|
||||
},
|
||||
},
|
||||
pinCard: {
|
||||
entry: 'storeContext',
|
||||
on: {
|
||||
STORE_RESPONSE: {
|
||||
actions: ['sendVcUpdated', 'VcUpdated'],
|
||||
target: 'idle',
|
||||
},
|
||||
},
|
||||
},
|
||||
kebabPopUp: {
|
||||
on: {
|
||||
DISMISS: {
|
||||
target: 'idle',
|
||||
},
|
||||
ADD_WALLET_BINDING_ID: {
|
||||
target: '#vc-item-openid4vci.kebabPopUp.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',
|
||||
},
|
||||
},
|
||||
initial: 'idle',
|
||||
states: {
|
||||
idle: {},
|
||||
showBindingWarning: {
|
||||
on: {
|
||||
CONFIRM: {
|
||||
target: '#vc-item-openid4vci.kebabPopUp.requestingBindingOtp',
|
||||
},
|
||||
CANCEL: {
|
||||
target: '#vc-item-openid4vci.kebabPopUp',
|
||||
},
|
||||
},
|
||||
},
|
||||
requestingBindingOtp: {
|
||||
invoke: {
|
||||
src: 'requestBindingOtp',
|
||||
onDone: [
|
||||
{
|
||||
target: '#vc-item-openid4vci.kebabPopUp.acceptingBindingOtp',
|
||||
actions: [log('accceptingOTP')],
|
||||
},
|
||||
],
|
||||
onError: [
|
||||
{
|
||||
actions: 'setWalletBindingError',
|
||||
target:
|
||||
'#vc-item-openid4vci.kebabPopUp.showingWalletBindingError',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
showingWalletBindingError: {
|
||||
on: {
|
||||
CANCEL: {
|
||||
target: '#vc-item-openid4vci.kebabPopUp',
|
||||
actions: 'setWalletBindingErrorEmpty',
|
||||
},
|
||||
},
|
||||
},
|
||||
acceptingBindingOtp: {
|
||||
entry: ['clearOtp'],
|
||||
on: {
|
||||
INPUT_OTP: {
|
||||
target: '#vc-item-openid4vci.kebabPopUp.addKeyPair',
|
||||
actions: ['setOtp'],
|
||||
},
|
||||
DISMISS: {
|
||||
target: '#vc-item-openid4vci.kebabPopUp',
|
||||
actions: ['clearOtp', 'clearTransactionId'],
|
||||
},
|
||||
},
|
||||
},
|
||||
addKeyPair: {
|
||||
invoke: {
|
||||
src: 'generateKeyPair',
|
||||
onDone: [
|
||||
{
|
||||
cond: 'isCustomSecureKeystore',
|
||||
target:
|
||||
'#vc-item-openid4vci.kebabPopUp.addingWalletBindingId',
|
||||
actions: ['setPublicKey'],
|
||||
},
|
||||
{
|
||||
target:
|
||||
'#vc-item-openid4vci.kebabPopUp.addingWalletBindingId',
|
||||
actions: ['setPublicKey', 'setPrivateKey'],
|
||||
},
|
||||
],
|
||||
onError: [
|
||||
{
|
||||
actions: 'setWalletBindingError',
|
||||
target:
|
||||
'#vc-item-openid4vci.kebabPopUp.showingWalletBindingError',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
addingWalletBindingId: {
|
||||
invoke: {
|
||||
src: 'addWalletBindnigId',
|
||||
onDone: [
|
||||
{
|
||||
cond: 'isCustomSecureKeystore',
|
||||
target: '#vc-item-openid4vci.kebabPopUp',
|
||||
actions: [
|
||||
'setWalletBindingId',
|
||||
'setThumbprintForWalletBindingId',
|
||||
'storeContext',
|
||||
'updateVc',
|
||||
'setWalletBindingErrorEmpty',
|
||||
'logWalletBindingSuccess',
|
||||
],
|
||||
},
|
||||
{
|
||||
target: '#vc-item-openid4vci.kebabPopUp.updatingPrivateKey',
|
||||
actions: [
|
||||
'setWalletBindingId',
|
||||
'setThumbprintForWalletBindingId',
|
||||
],
|
||||
},
|
||||
],
|
||||
onError: [
|
||||
{
|
||||
actions: ['setWalletBindingError', 'logWalletBindingFailure'],
|
||||
target:
|
||||
'#vc-item-openid4vci.kebabPopUp.showingWalletBindingError',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
updatingPrivateKey: {
|
||||
invoke: {
|
||||
src: 'updatePrivateKey',
|
||||
onDone: {
|
||||
actions: [
|
||||
'storeContext',
|
||||
'updatePrivateKey',
|
||||
'updateVc',
|
||||
'setWalletBindingErrorEmpty',
|
||||
'logWalletBindingSuccess',
|
||||
],
|
||||
target: '#vc-item-openid4vci.kebabPopUp',
|
||||
},
|
||||
onError: {
|
||||
actions: 'setWalletBindingError',
|
||||
target:
|
||||
'#vc-item-openid4vci.kebabPopUp.showingWalletBindingError',
|
||||
},
|
||||
},
|
||||
},
|
||||
showActivities: {
|
||||
on: {
|
||||
DISMISS: '#vc-item-openid4vci.kebabPopUp',
|
||||
},
|
||||
},
|
||||
removeWallet: {
|
||||
on: {
|
||||
CONFIRM: {
|
||||
target: 'removingVc',
|
||||
},
|
||||
CANCEL: {
|
||||
target: 'idle',
|
||||
},
|
||||
},
|
||||
},
|
||||
removingVc: {
|
||||
entry: 'removeVcItem',
|
||||
on: {
|
||||
STORE_RESPONSE: {
|
||||
actions: ['removedVc', 'logVCremoved'],
|
||||
target: '#vc-item-openid4vci',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
actions: {
|
||||
requestVcContext: send(
|
||||
context => ({
|
||||
type: 'GET_VC_ITEM',
|
||||
vcMetadata: context.vcMetadata,
|
||||
protocol: OpenId4VCIProtocol,
|
||||
}),
|
||||
{
|
||||
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 {verifiableCredential} = context;
|
||||
return {
|
||||
type: 'VC_DOWNLOADED_FROM_OPENID4VCI',
|
||||
verifiableCredential,
|
||||
};
|
||||
},
|
||||
{
|
||||
to: context => context.serviceRefs.vc,
|
||||
},
|
||||
),
|
||||
|
||||
setVerifiableCredential: model.assign({
|
||||
verifiableCredential: (_, event) => {
|
||||
if (event.type === 'GET_VC_RESPONSE') {
|
||||
return event.vc;
|
||||
}
|
||||
return event.response;
|
||||
},
|
||||
}),
|
||||
|
||||
setGeneratedOn: model.assign({
|
||||
generatedOn: (context, _event) => {
|
||||
return context.verifiableCredential.generatedOn;
|
||||
},
|
||||
}),
|
||||
|
||||
storeContext: send(
|
||||
context => {
|
||||
const {serviceRefs, ...data} = context;
|
||||
data.credentialRegistry = MIMOTO_BASE_URL;
|
||||
return StoreEvents.SET(
|
||||
VCMetadata.fromVC(context, true).getVcKey(),
|
||||
data,
|
||||
);
|
||||
},
|
||||
{
|
||||
to: context => context.serviceRefs.store,
|
||||
},
|
||||
),
|
||||
setPinCard: assign(context => {
|
||||
return {
|
||||
...context,
|
||||
isPinned: !context.isPinned,
|
||||
};
|
||||
}),
|
||||
VcUpdated: send(
|
||||
context => {
|
||||
const {serviceRefs, ...vc} = context;
|
||||
return {type: 'VC_UPDATE', vc};
|
||||
},
|
||||
{
|
||||
to: context => context.serviceRefs.vc,
|
||||
},
|
||||
),
|
||||
|
||||
sendVcUpdated: send(
|
||||
context =>
|
||||
VcEvents.VC_METADATA_UPDATED(
|
||||
new VCMetadata({...context.vcMetadata, isPinned: context.isPinned}),
|
||||
),
|
||||
{
|
||||
to: context => context.serviceRefs.vc,
|
||||
},
|
||||
),
|
||||
|
||||
setWalletBindingError: assign({
|
||||
walletBindingError: (context, event) =>
|
||||
i18n.t(`errors.genericError`, {
|
||||
ns: 'common',
|
||||
}),
|
||||
}),
|
||||
setWalletBindingErrorEmpty: assign({
|
||||
walletBindingError: () => '',
|
||||
}),
|
||||
setPublicKey: assign({
|
||||
publicKey: (context, event) => {
|
||||
if (!isCustomSecureKeystore()) {
|
||||
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) =>
|
||||
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,
|
||||
},
|
||||
),
|
||||
logDownloaded: send(
|
||||
context => {
|
||||
const {serviceRefs, ...data} = context;
|
||||
return ActivityLogEvents.LOG_ACTIVITY({
|
||||
_vcKey: VCMetadata.fromVC(data, true).getVcKey(),
|
||||
type: 'VC_DOWNLOADED',
|
||||
timestamp: Date.now(),
|
||||
deviceName: '',
|
||||
vcLabel: data.id,
|
||||
});
|
||||
},
|
||||
{
|
||||
to: context => context.serviceRefs.activityLog,
|
||||
},
|
||||
),
|
||||
logWalletBindingSuccess: send(
|
||||
context =>
|
||||
ActivityLogEvents.LOG_ACTIVITY({
|
||||
_vcKey: VCMetadata.fromVC(context, true).getVcKey(),
|
||||
type: 'WALLET_BINDING_SUCCESSFULL',
|
||||
timestamp: Date.now(),
|
||||
deviceName: '',
|
||||
vcLabel: context.id,
|
||||
}),
|
||||
{
|
||||
to: context => context.serviceRefs.activityLog,
|
||||
},
|
||||
),
|
||||
|
||||
logWalletBindingFailure: send(
|
||||
context =>
|
||||
ActivityLogEvents.LOG_ACTIVITY({
|
||||
_vcKey: VCMetadata.fromVC(context, true).getVcKey(),
|
||||
type: 'WALLET_BINDING_FAILURE',
|
||||
timestamp: Date.now(),
|
||||
deviceName: '',
|
||||
vcLabel: context.id,
|
||||
}),
|
||||
{
|
||||
to: context => context.serviceRefs.activityLog,
|
||||
},
|
||||
),
|
||||
setOtp: model.assign({
|
||||
otp: (_, event) => event.otp,
|
||||
}),
|
||||
|
||||
setOtpError: assign({
|
||||
otpError: (_context, event) =>
|
||||
(event as ErrorPlatformEvent).data.message,
|
||||
}),
|
||||
|
||||
clearOtp: assign({otp: ''}),
|
||||
removeVcItem: send(
|
||||
_context => {
|
||||
return StoreEvents.REMOVE(
|
||||
MY_VCS_STORE_KEY,
|
||||
_context.vcMetadata.getVcKey(),
|
||||
);
|
||||
},
|
||||
{to: context => context.serviceRefs.store},
|
||||
),
|
||||
setVcKey: model.assign({
|
||||
vcMetadata: (_, event) => event.vcMetadata,
|
||||
}),
|
||||
logVCremoved: send(
|
||||
(context, _) =>
|
||||
ActivityLogEvents.LOG_ACTIVITY({
|
||||
_vcKey: VCMetadata.fromVC(context).getVcKey(),
|
||||
type: 'VC_REMOVED',
|
||||
timestamp: Date.now(),
|
||||
deviceName: '',
|
||||
vcLabel: context.id,
|
||||
}),
|
||||
{
|
||||
to: context => context.serviceRefs.activityLog,
|
||||
},
|
||||
),
|
||||
},
|
||||
|
||||
services: {
|
||||
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 '';
|
||||
},
|
||||
addWalletBindnigId: async context => {
|
||||
const response = await request(
|
||||
'POST',
|
||||
'/residentmobileapp/wallet-binding',
|
||||
{
|
||||
requestTime: String(new Date().toISOString()),
|
||||
request: {
|
||||
authFactorType: 'WLA',
|
||||
format: 'jwt',
|
||||
individualId:
|
||||
context.verifiableCredential.credential.credentialSubject.VID,
|
||||
transactionId: context.transactionId,
|
||||
publicKey: context.publicKey,
|
||||
challengeList: [
|
||||
{
|
||||
authFactorType: 'OTP',
|
||||
challenge: context.otp,
|
||||
format: 'alpha-numeric',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
);
|
||||
const certificate = response.response.certificate;
|
||||
await savePrivateKey(
|
||||
getBindingCertificateConstant(context.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 (!isCustomSecureKeystore()) {
|
||||
return await generateKeys();
|
||||
}
|
||||
const isBiometricsEnabled = SecureKeystore.hasBiometricsEnabled();
|
||||
return SecureKeystore.generateKeyPair(
|
||||
context.id,
|
||||
isBiometricsEnabled,
|
||||
0,
|
||||
);
|
||||
},
|
||||
requestBindingOtp: async context => {
|
||||
const response = await request(
|
||||
'POST',
|
||||
'/residentmobileapp/binding-otp',
|
||||
{
|
||||
requestTime: String(new Date().toISOString()),
|
||||
request: {
|
||||
individualId:
|
||||
context.verifiableCredential.credential.credentialSubject.VID,
|
||||
otpChannels: ['EMAIL', 'PHONE'],
|
||||
},
|
||||
},
|
||||
);
|
||||
if (response.response == null) {
|
||||
throw new Error('Could not process request');
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
guards: {
|
||||
hasCredential: (_, event) => {
|
||||
const vc = event.vc;
|
||||
return vc != null;
|
||||
},
|
||||
|
||||
isCustomSecureKeystore: () => isCustomSecureKeystore(),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export const createEsignetMosipVCItemMachine = (
|
||||
serviceRefs: AppServices,
|
||||
vcMetadata: VCMetadata,
|
||||
) => {
|
||||
return EsignetMosipVCItemMachine.withContext({
|
||||
...EsignetMosipVCItemMachine.context,
|
||||
serviceRefs,
|
||||
vcMetadata,
|
||||
});
|
||||
};
|
||||
|
||||
type State = StateFrom<typeof EsignetMosipVCItemMachine>;
|
||||
|
||||
export function selectVerifiableCredentials(state: State) {
|
||||
return state.context.verifiableCredential;
|
||||
}
|
||||
|
||||
export function selectKebabPopUp(state: State) {
|
||||
return state.matches('kebabPopUp');
|
||||
}
|
||||
|
||||
export function selectContext(state: State) {
|
||||
return state.context;
|
||||
}
|
||||
|
||||
export function selectGeneratedOn(state: State) {
|
||||
return new Date(state.context.generatedOn).toLocaleDateString();
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
// 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.addKeyPair:invocation[0]': {
|
||||
type: 'done.invoke.vc-item-openid4vci.kebabPopUp.addKeyPair:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'done.invoke.vc-item-openid4vci.kebabPopUp.addingWalletBindingId:invocation[0]': {
|
||||
type: 'done.invoke.vc-item-openid4vci.kebabPopUp.addingWalletBindingId:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'done.invoke.vc-item-openid4vci.kebabPopUp.requestingBindingOtp:invocation[0]': {
|
||||
type: 'done.invoke.vc-item-openid4vci.kebabPopUp.requestingBindingOtp:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'done.invoke.vc-item-openid4vci.kebabPopUp.updatingPrivateKey:invocation[0]': {
|
||||
type: 'done.invoke.vc-item-openid4vci.kebabPopUp.updatingPrivateKey: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.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.kebabPopUp.addKeyPair:invocation[0]': {
|
||||
type: 'error.platform.vc-item-openid4vci.kebabPopUp.addKeyPair:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'error.platform.vc-item-openid4vci.kebabPopUp.addingWalletBindingId:invocation[0]': {
|
||||
type: 'error.platform.vc-item-openid4vci.kebabPopUp.addingWalletBindingId:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'error.platform.vc-item-openid4vci.kebabPopUp.requestingBindingOtp:invocation[0]': {
|
||||
type: 'error.platform.vc-item-openid4vci.kebabPopUp.requestingBindingOtp:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'error.platform.vc-item-openid4vci.kebabPopUp.updatingPrivateKey:invocation[0]': {
|
||||
type: 'error.platform.vc-item-openid4vci.kebabPopUp.updatingPrivateKey: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]'
|
||||
| 'done.invoke.vc-item-openid4vci.kebabPopUp.addingWalletBindingId:invocation[0]';
|
||||
generateKeyPair:
|
||||
| 'done.invoke.vc-item-openid4vci.addKeyPair:invocation[0]'
|
||||
| 'done.invoke.vc-item-openid4vci.kebabPopUp.addKeyPair:invocation[0]';
|
||||
requestBindingOtp:
|
||||
| 'done.invoke.vc-item-openid4vci.kebabPopUp.requestingBindingOtp:invocation[0]'
|
||||
| 'done.invoke.vc-item-openid4vci.requestingBindingOtp:invocation[0]';
|
||||
updatePrivateKey:
|
||||
| 'done.invoke.vc-item-openid4vci.kebabPopUp.updatingPrivateKey:invocation[0]'
|
||||
| 'done.invoke.vc-item-openid4vci.updatingPrivateKey:invocation[0]';
|
||||
};
|
||||
missingImplementations: {
|
||||
actions: 'clearTransactionId';
|
||||
delays: never;
|
||||
guards: never;
|
||||
services: never;
|
||||
};
|
||||
eventsCausingActions: {
|
||||
VcUpdated: 'STORE_RESPONSE';
|
||||
clearOtp:
|
||||
| 'DISMISS'
|
||||
| 'done.invoke.vc-item-openid4vci.kebabPopUp.requestingBindingOtp:invocation[0]'
|
||||
| 'done.invoke.vc-item-openid4vci.requestingBindingOtp:invocation[0]';
|
||||
clearTransactionId: 'DISMISS';
|
||||
logVCremoved: 'STORE_RESPONSE';
|
||||
logWalletBindingFailure:
|
||||
| 'error.platform.vc-item-openid4vci.addKeyPair:invocation[0]'
|
||||
| 'error.platform.vc-item-openid4vci.addingWalletBindingId:invocation[0]'
|
||||
| 'error.platform.vc-item-openid4vci.kebabPopUp.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.kebabPopUp.addingWalletBindingId:invocation[0]'
|
||||
| 'done.invoke.vc-item-openid4vci.kebabPopUp.updatingPrivateKey:invocation[0]'
|
||||
| 'done.invoke.vc-item-openid4vci.updatingPrivateKey:invocation[0]';
|
||||
removeVcItem: 'CONFIRM';
|
||||
removedVc: 'STORE_RESPONSE';
|
||||
requestStoredContext: 'GET_VC_RESPONSE' | 'REFRESH';
|
||||
requestVcContext: 'DISMISS' | 'xstate.init';
|
||||
sendVcUpdated: 'STORE_RESPONSE';
|
||||
setGeneratedOn: 'GET_VC_RESPONSE' | 'STORE_RESPONSE';
|
||||
setOtp: 'INPUT_OTP';
|
||||
setPinCard: 'PIN_CARD';
|
||||
setPrivateKey:
|
||||
| 'done.invoke.vc-item-openid4vci.addKeyPair:invocation[0]'
|
||||
| 'done.invoke.vc-item-openid4vci.kebabPopUp.addKeyPair:invocation[0]';
|
||||
setPublicKey:
|
||||
| 'done.invoke.vc-item-openid4vci.addKeyPair:invocation[0]'
|
||||
| 'done.invoke.vc-item-openid4vci.kebabPopUp.addKeyPair:invocation[0]';
|
||||
setThumbprintForWalletBindingId:
|
||||
| 'done.invoke.vc-item-openid4vci.addingWalletBindingId:invocation[0]'
|
||||
| 'done.invoke.vc-item-openid4vci.kebabPopUp.addingWalletBindingId:invocation[0]';
|
||||
setVcKey: 'REMOVE';
|
||||
setVerifiableCredential: 'GET_VC_RESPONSE' | 'STORE_RESPONSE';
|
||||
setWalletBindingError:
|
||||
| 'error.platform.vc-item-openid4vci.addKeyPair:invocation[0]'
|
||||
| 'error.platform.vc-item-openid4vci.addingWalletBindingId:invocation[0]'
|
||||
| 'error.platform.vc-item-openid4vci.kebabPopUp.addKeyPair:invocation[0]'
|
||||
| 'error.platform.vc-item-openid4vci.kebabPopUp.addingWalletBindingId:invocation[0]'
|
||||
| 'error.platform.vc-item-openid4vci.kebabPopUp.requestingBindingOtp:invocation[0]'
|
||||
| 'error.platform.vc-item-openid4vci.kebabPopUp.updatingPrivateKey: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.kebabPopUp.addingWalletBindingId:invocation[0]'
|
||||
| 'done.invoke.vc-item-openid4vci.kebabPopUp.updatingPrivateKey:invocation[0]'
|
||||
| 'done.invoke.vc-item-openid4vci.updatingPrivateKey:invocation[0]';
|
||||
setWalletBindingId:
|
||||
| 'done.invoke.vc-item-openid4vci.addingWalletBindingId:invocation[0]'
|
||||
| 'done.invoke.vc-item-openid4vci.kebabPopUp.addingWalletBindingId:invocation[0]';
|
||||
storeContext:
|
||||
| 'PIN_CARD'
|
||||
| 'done.invoke.vc-item-openid4vci.addingWalletBindingId:invocation[0]'
|
||||
| 'done.invoke.vc-item-openid4vci.kebabPopUp.addingWalletBindingId:invocation[0]'
|
||||
| 'done.invoke.vc-item-openid4vci.kebabPopUp.updatingPrivateKey:invocation[0]'
|
||||
| 'done.invoke.vc-item-openid4vci.updatingPrivateKey:invocation[0]';
|
||||
updatePrivateKey:
|
||||
| 'done.invoke.vc-item-openid4vci.kebabPopUp.updatingPrivateKey: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.kebabPopUp.addingWalletBindingId:invocation[0]'
|
||||
| 'done.invoke.vc-item-openid4vci.kebabPopUp.updatingPrivateKey: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]'
|
||||
| 'done.invoke.vc-item-openid4vci.kebabPopUp.addKeyPair:invocation[0]'
|
||||
| 'done.invoke.vc-item-openid4vci.kebabPopUp.addingWalletBindingId:invocation[0]';
|
||||
};
|
||||
eventsCausingServices: {
|
||||
addWalletBindnigId:
|
||||
| 'done.invoke.vc-item-openid4vci.addKeyPair:invocation[0]'
|
||||
| 'done.invoke.vc-item-openid4vci.kebabPopUp.addKeyPair:invocation[0]';
|
||||
generateKeyPair: 'INPUT_OTP';
|
||||
requestBindingOtp: 'CONFIRM';
|
||||
updatePrivateKey:
|
||||
| 'done.invoke.vc-item-openid4vci.addingWalletBindingId:invocation[0]'
|
||||
| 'done.invoke.vc-item-openid4vci.kebabPopUp.addingWalletBindingId:invocation[0]';
|
||||
};
|
||||
matchesStates:
|
||||
| 'acceptingBindingOtp'
|
||||
| 'addKeyPair'
|
||||
| 'addingWalletBindingId'
|
||||
| 'checkingStore'
|
||||
| 'checkingVc'
|
||||
| 'idle'
|
||||
| 'kebabPopUp'
|
||||
| 'kebabPopUp.acceptingBindingOtp'
|
||||
| 'kebabPopUp.addKeyPair'
|
||||
| 'kebabPopUp.addingWalletBindingId'
|
||||
| 'kebabPopUp.idle'
|
||||
| 'kebabPopUp.removeWallet'
|
||||
| 'kebabPopUp.removingVc'
|
||||
| 'kebabPopUp.requestingBindingOtp'
|
||||
| 'kebabPopUp.showActivities'
|
||||
| 'kebabPopUp.showBindingWarning'
|
||||
| 'kebabPopUp.showingWalletBindingError'
|
||||
| 'kebabPopUp.updatingPrivateKey'
|
||||
| 'pinCard'
|
||||
| 'requestingBindingOtp'
|
||||
| 'showBindingWarning'
|
||||
| 'showingWalletBindingError'
|
||||
| 'updatingPrivateKey'
|
||||
| {
|
||||
kebabPopUp?:
|
||||
| 'acceptingBindingOtp'
|
||||
| 'addKeyPair'
|
||||
| 'addingWalletBindingId'
|
||||
| 'idle'
|
||||
| 'removeWallet'
|
||||
| 'removingVc'
|
||||
| 'requestingBindingOtp'
|
||||
| 'showActivities'
|
||||
| 'showBindingWarning'
|
||||
| 'showingWalletBindingError'
|
||||
| 'updatingPrivateKey';
|
||||
};
|
||||
tags: never;
|
||||
}
|
||||
@@ -1,41 +1,45 @@
|
||||
import {assign, ErrorPlatformEvent, EventFrom, send, StateFrom} from 'xstate';
|
||||
import {createModel} from 'xstate/lib/model';
|
||||
import {HOST, MIMOTO_BASE_URL, MY_VCS_STORE_KEY} from '../shared/constants';
|
||||
import {AppServices} from '../shared/GlobalContext';
|
||||
import {CredentialDownloadResponse, request} from '../shared/request';
|
||||
import {
|
||||
HOST,
|
||||
MIMOTO_BASE_URL,
|
||||
MY_VCS_STORE_KEY,
|
||||
} from '../../../shared/constants';
|
||||
import {AppServices} from '../../../shared/GlobalContext';
|
||||
import {CredentialDownloadResponse, request} from '../../../shared/request';
|
||||
import {
|
||||
VC,
|
||||
VerifiableCredential,
|
||||
VcIdType,
|
||||
DecodedCredential,
|
||||
} from '../types/vc';
|
||||
import {StoreEvents} from './store';
|
||||
import {ActivityLogEvents} from './activityLog';
|
||||
import {verifyCredential} from '../shared/vcjs/verifyCredential';
|
||||
} from '../../../types/vc';
|
||||
import {StoreEvents} from '../../store';
|
||||
import {ActivityLogEvents} from '../../activityLog';
|
||||
import {verifyCredential} from '../../../shared/vcjs/verifyCredential';
|
||||
import {log} from 'xstate/lib/actions';
|
||||
import {
|
||||
generateKeys,
|
||||
isCustomSecureKeystore,
|
||||
WalletBindingResponse,
|
||||
} from '../shared/cryptoutil/cryptoUtil';
|
||||
} from '../../../shared/cryptoutil/cryptoUtil';
|
||||
import {KeyPair} from 'react-native-rsa-native';
|
||||
import {
|
||||
getBindingCertificateConstant,
|
||||
savePrivateKey,
|
||||
} from '../shared/keystore/SecureKeystore';
|
||||
} from '../../../shared/keystore/SecureKeystore';
|
||||
import getAllConfigurations, {
|
||||
DownloadProps,
|
||||
} from '../shared/commonprops/commonProps';
|
||||
import {VcEvents} from './vc';
|
||||
import i18n from '../i18n';
|
||||
} from '../../../shared/commonprops/commonProps';
|
||||
import {VcEvents} from '../../vc';
|
||||
import i18n from '../../../i18n';
|
||||
import SecureKeystore from 'react-native-secure-keystore';
|
||||
import {VCMetadata} from '../shared/VCMetadata';
|
||||
import {VCMetadata} from '../../../shared/VCMetadata';
|
||||
import {
|
||||
sendStartEvent,
|
||||
getData,
|
||||
getEndData,
|
||||
sendEndEvent,
|
||||
} from '../shared/telemetry/TelemetryUtils';
|
||||
} from '../../../shared/telemetry/TelemetryUtils';
|
||||
|
||||
const model = createModel(
|
||||
{
|
||||
@@ -98,15 +102,15 @@ const model = createModel(
|
||||
},
|
||||
);
|
||||
|
||||
export const VcItemEvents = model.events;
|
||||
export const ExistingMosipVCItemEvents = model.events;
|
||||
|
||||
export const vcItemMachine =
|
||||
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('./vcItem.typegen').Typegen0,
|
||||
tsTypes: {} as import('./ExistingMosipVCItemMachine.typegen').Typegen0,
|
||||
schema: {
|
||||
context: model.initialContext,
|
||||
events: {} as EventFrom<typeof model>,
|
||||
@@ -1339,12 +1343,12 @@ export const vcItemMachine =
|
||||
},
|
||||
);
|
||||
|
||||
export const createVcItemMachine = (
|
||||
export const createExistingMosipVCItemMachine = (
|
||||
serviceRefs: AppServices,
|
||||
vcMetadata: VCMetadata,
|
||||
) => {
|
||||
return vcItemMachine.withContext({
|
||||
...vcItemMachine.context,
|
||||
return ExistingMosipVCItemMachine.withContext({
|
||||
...ExistingMosipVCItemMachine.context,
|
||||
serviceRefs,
|
||||
id: vcMetadata.id,
|
||||
idType: vcMetadata.idType as VcIdType,
|
||||
@@ -1353,7 +1357,7 @@ export const createVcItemMachine = (
|
||||
});
|
||||
};
|
||||
|
||||
type State = StateFrom<typeof vcItemMachine>;
|
||||
type State = StateFrom<typeof ExistingMosipVCItemMachine>;
|
||||
|
||||
export function selectVc(state: State) {
|
||||
const {serviceRefs, ...data} = state.context;
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
export interface Typegen0 {
|
||||
'@@xstate/typegen': true;
|
||||
'internalEvents': {
|
||||
'': { type: '' };
|
||||
internalEvents: {
|
||||
'': {type: ''};
|
||||
'done.invoke.checkStatus': {
|
||||
type: 'done.invoke.checkStatus';
|
||||
data: unknown;
|
||||
@@ -135,9 +135,9 @@ export interface Typegen0 {
|
||||
type: 'error.platform.vc-item.verifyingCredential:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'xstate.init': { type: 'xstate.init' };
|
||||
'xstate.init': {type: 'xstate.init'};
|
||||
};
|
||||
'invokeSrcNameMap': {
|
||||
invokeSrcNameMap: {
|
||||
addWalletBindnigId:
|
||||
| 'done.invoke.vc-item.addingWalletBindingId:invocation[0]'
|
||||
| 'done.invoke.vc-item.kebabPopUp.addingWalletBindingId:invocation[0]';
|
||||
@@ -158,14 +158,13 @@ export interface Typegen0 {
|
||||
| 'done.invoke.vc-item.updatingPrivateKey:invocation[0]';
|
||||
verifyCredential: 'done.invoke.vc-item.verifyingCredential:invocation[0]';
|
||||
};
|
||||
'missingImplementations': {
|
||||
missingImplementations: {
|
||||
actions: never;
|
||||
delays: never;
|
||||
guards: never;
|
||||
services: never;
|
||||
};
|
||||
'eventsCausingActions': {
|
||||
VcUpdated: 'STORE_RESPONSE';
|
||||
eventsCausingActions: {
|
||||
clearOtp:
|
||||
| ''
|
||||
| 'CANCEL'
|
||||
@@ -190,7 +189,6 @@ export interface Typegen0 {
|
||||
| 'done.invoke.vc-item.updatingPrivateKey:invocation[0]'
|
||||
| 'done.invoke.vc-item.verifyingCredential:invocation[0]'
|
||||
| 'error.platform.vc-item.verifyingCredential:invocation[0]';
|
||||
editVcKey: 'CREDENTIAL_DOWNLOADED';
|
||||
incrementDownloadCounter: 'POLL';
|
||||
logDownloaded: 'STORE_RESPONSE';
|
||||
logRevoked: 'STORE_RESPONSE';
|
||||
@@ -214,7 +212,7 @@ export interface Typegen0 {
|
||||
requestStoredContext: 'GET_VC_RESPONSE' | 'REFRESH';
|
||||
requestVcContext: 'DISMISS' | 'xstate.init';
|
||||
revokeVID: 'done.invoke.vc-item.requestingRevoke:invocation[0]';
|
||||
sendVcUpdated: 'STORE_RESPONSE';
|
||||
sendVcUpdated: 'PIN_CARD';
|
||||
setCredential: 'GET_VC_RESPONSE' | 'STORE_RESPONSE';
|
||||
setDownloadInterval: 'done.invoke.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]';
|
||||
setLock: 'done.invoke.vc-item.requestingLock:invocation[0]';
|
||||
@@ -264,7 +262,6 @@ export interface Typegen0 {
|
||||
| 'done.invoke.vc-item.kebabPopUp.addingWalletBindingId:invocation[0]';
|
||||
storeContext:
|
||||
| 'CREDENTIAL_DOWNLOADED'
|
||||
| 'PIN_CARD'
|
||||
| 'done.invoke.vc-item.addingWalletBindingId:invocation[0]'
|
||||
| 'done.invoke.vc-item.kebabPopUp.addingWalletBindingId:invocation[0]'
|
||||
| 'done.invoke.vc-item.kebabPopUp.updatingPrivateKey:invocation[0]'
|
||||
@@ -283,8 +280,8 @@ export interface Typegen0 {
|
||||
| 'done.invoke.vc-item.updatingPrivateKey:invocation[0]'
|
||||
| 'done.invoke.vc-item.verifyingCredential:invocation[0]';
|
||||
};
|
||||
'eventsCausingDelays': {};
|
||||
'eventsCausingGuards': {
|
||||
eventsCausingDelays: {};
|
||||
eventsCausingGuards: {
|
||||
hasCredential: 'GET_VC_RESPONSE' | 'STORE_RESPONSE';
|
||||
isCustomSecureKeystore:
|
||||
| 'done.invoke.vc-item.addKeyPair:invocation[0]'
|
||||
@@ -294,7 +291,7 @@ export interface Typegen0 {
|
||||
isDownloadAllowed: 'POLL';
|
||||
isVcValid: '';
|
||||
};
|
||||
'eventsCausingServices': {
|
||||
eventsCausingServices: {
|
||||
addWalletBindnigId:
|
||||
| 'done.invoke.vc-item.addKeyPair:invocation[0]'
|
||||
| 'done.invoke.vc-item.kebabPopUp.addKeyPair:invocation[0]';
|
||||
@@ -313,7 +310,7 @@ export interface Typegen0 {
|
||||
| 'done.invoke.vc-item.kebabPopUp.addingWalletBindingId:invocation[0]';
|
||||
verifyCredential: '' | 'VERIFY';
|
||||
};
|
||||
'matchesStates':
|
||||
matchesStates:
|
||||
| 'acceptingBindingOtp'
|
||||
| 'acceptingOtpInput'
|
||||
| 'acceptingRevokeInput'
|
||||
@@ -365,7 +362,7 @@ export interface Typegen0 {
|
||||
| 'downloadingCredential'
|
||||
| 'savingFailed'
|
||||
| 'verifyingDownloadLimitExpiry'
|
||||
| { savingFailed?: 'idle' | 'viewingVc' };
|
||||
| {savingFailed?: 'idle' | 'viewingVc'};
|
||||
invalid?: 'backend' | 'otp';
|
||||
kebabPopUp?:
|
||||
| 'acceptingBindingOtp'
|
||||
@@ -380,5 +377,5 @@ export interface Typegen0 {
|
||||
| 'showingWalletBindingError'
|
||||
| 'updatingPrivateKey';
|
||||
};
|
||||
'tags': never;
|
||||
tags: never;
|
||||
}
|
||||
501
machines/issuersMachine.ts
Normal file
501
machines/issuersMachine.ts
Normal file
@@ -0,0 +1,501 @@
|
||||
import {authorize, AuthorizeResult} from 'react-native-app-auth';
|
||||
import {assign, EventFrom, send, sendParent, StateFrom} from 'xstate';
|
||||
import {createModel} from 'xstate/lib/model';
|
||||
import {Theme} from '../components/ui/styleUtils';
|
||||
import {MY_VCS_STORE_KEY} from '../shared/constants';
|
||||
import {request} from '../shared/request';
|
||||
import {StoreEvents} from './store';
|
||||
import {AppServices} from '../shared/GlobalContext';
|
||||
import {
|
||||
generateKeys,
|
||||
isCustomSecureKeystore,
|
||||
} from '../shared/cryptoutil/cryptoUtil';
|
||||
import SecureKeystore from 'react-native-secure-keystore';
|
||||
import {KeyPair} from 'react-native-rsa-native';
|
||||
import {ActivityLogEvents} from './activityLog';
|
||||
import {log} from 'xstate/lib/actions';
|
||||
import {verifyCredential} from '../shared/vcjs/verifyCredential';
|
||||
import {getBody, getIdentifier} from '../shared/openId4VCI/Utils';
|
||||
import {VCMetadata} from '../shared/VCMetadata';
|
||||
import {VerifiableCredential} from '../components/VC/EsignetMosipVCItem/vc';
|
||||
|
||||
const model = createModel(
|
||||
{
|
||||
issuers: [] as issuerType[],
|
||||
selectedIssuer: [] as issuerType[],
|
||||
tokenResponse: {} as AuthorizeResult,
|
||||
errorMessage: '' as string,
|
||||
loadingReason: 'displayIssuers' as string,
|
||||
verifiableCredential: null as VerifiableCredential | null,
|
||||
serviceRefs: {} as AppServices,
|
||||
|
||||
publicKey: ``,
|
||||
privateKey: ``,
|
||||
},
|
||||
{
|
||||
events: {
|
||||
DISMISS: () => ({}),
|
||||
SELECTED_ISSUER: (id: string) => ({id}),
|
||||
DOWNLOAD_ID: () => ({}),
|
||||
COMPLETED: () => ({}),
|
||||
TRY_AGAIN: () => ({}),
|
||||
RESET_ERROR: () => ({}),
|
||||
CHECK_KEY_PAIR: () => ({}),
|
||||
CANCEL: () => ({}),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export const IssuerScreenTabEvents = model.events;
|
||||
export const Issuer_Tab_Ref_Id = 'issuersMachine';
|
||||
|
||||
export const Issuers_Key_Ref = 'OpenId4VCI';
|
||||
export const IssuersMachine = model.createMachine(
|
||||
{
|
||||
/** @xstate-layout N4IgpgJg5mDOIC5QEtawK5gE6wLIEMBjAC2QDswA6AG1QBcBJNTHAYggHsLLY786qqDNjxFS3WrybCcAbQAMAXUSgADh1jI6yLipAAPRAEYA7PMomj8gJwAWeSYAcTgMy2TJgDQgAnsZPWlLYArC5GAEzWEfKRtka2AL4J3kIsoiTkVLBg1GCE2mRQ0iysACIA8gDqAHIAMuUAgqUA+gBqDA3NDKUKykgg6prauv2GCMHuFo7W8sET8i4x1uHefgimJpTWoRHWJrYuc9YAbEkpzCIEGdzZufnkRRdYrADKAKK1bwDCACpvLQwXi8AKpvABKvT0gy0OjIejGE02Thmc3si0iK18iEcRiC1nxJxR22OjhcZxAqUuYkyPByeQKjxkZUBuEBL0h-Whwzho0QiKmKPm6OWq0Q4X2QXkx3C0uCOKM0pcJnJlJwV3EWTp+WK2HYXCyfAElFV6Q1tLujCeHLUGhhI1AY2mx0oRmCxyMjgcJMi8VF6xM4Uo4QWRjc2xMcqVp2SFKepppqmwADMOFgALYNdB0Yip5AAL34sNYDWBPwAEuUwQwAFr-a0DW3c+HGBaOILHeSOWwkj3hYIKv34rYmY4TUeOZzSxzR84yePcRNYFPpzPZ3MF7msMHfN4MVr-Zo-coAaTe1XrXNhzfW4VvlEW3YVMRcexlfo2Wx2kX2h2C20SMYmuqNIwHQXxYJAYBkNo+DUAAYlgHBpjqzycDchqCHGwHcKB4GQdByCwQhSEoRejZXry6zuG2yy9scSq0bY75WEGMpytM+wONsZLkmQHAQHAehAdSFBQuR9oGIgAC0xx+jJKpYSJghkFoYlDBRDqIMctiUB2EyOPpMzhM4mJrH2gTLHs8jxI44T2K6ClzthVCSJac5qXaPKaQgtimWK1lBJExwzF24QuMGtgAbOaTOea9IPChHlNpRLi2ZQ-Zur5o7PlEslYggwa4r5JwBFlLijvsjkxUpcXak8SUaZJCARi4Lq2F28izGEna2e+dgugExwmKl7hmFKVVUtcVCLsuGZZjmWD5oWEmXhJYxWCxtkxGiRjLJYjh+oVgUlXYMrlcElWAYpU2ULhEECQRRGIch9WcuJXlNfECqBR6MTOHYZgHflwZtkNVkxGdrbhBNao1cgEC5A1a2IC1bUdV1VgTn5BV7JQWOksENhyk4RhJEkQA */
|
||||
predictableActionArguments: true,
|
||||
preserveActionOrder: true,
|
||||
id: Issuer_Tab_Ref_Id,
|
||||
context: model.initialContext,
|
||||
initial: 'displayIssuers',
|
||||
tsTypes: {} as import('./issuersMachine.typegen').Typegen0,
|
||||
schema: {
|
||||
context: model.initialContext,
|
||||
events: {} as EventFrom<typeof model>,
|
||||
},
|
||||
states: {
|
||||
displayIssuers: {
|
||||
description: 'displays the issuers downloaded from the server',
|
||||
invoke: {
|
||||
src: 'downloadIssuersList',
|
||||
onDone: {
|
||||
actions: ['setIssuers'],
|
||||
target: 'selectingIssuer',
|
||||
},
|
||||
onError: {
|
||||
actions: ['setError'],
|
||||
target: 'error',
|
||||
},
|
||||
},
|
||||
},
|
||||
error: {
|
||||
description: 'reaches here when any error happens',
|
||||
on: {
|
||||
TRY_AGAIN: {
|
||||
actions: 'resetError',
|
||||
target: 'displayIssuers',
|
||||
},
|
||||
RESET_ERROR: {
|
||||
actions: 'resetError',
|
||||
target: 'idle',
|
||||
},
|
||||
},
|
||||
},
|
||||
selectingIssuer: {
|
||||
description: 'waits for the user to select any issuer',
|
||||
on: {
|
||||
DOWNLOAD_ID: {
|
||||
actions: sendParent('DOWNLOAD_ID'),
|
||||
},
|
||||
SELECTED_ISSUER: {
|
||||
target: 'downloadIssuerConfig',
|
||||
},
|
||||
},
|
||||
},
|
||||
downloadIssuerConfig: {
|
||||
description: 'downloads the configuration of the selected issuer',
|
||||
invoke: {
|
||||
src: 'downloadIssuerConfig',
|
||||
onDone: {
|
||||
actions: 'setSelectedIssuers',
|
||||
target: 'performAuthorization',
|
||||
},
|
||||
},
|
||||
},
|
||||
performAuthorization: {
|
||||
description:
|
||||
'invokes the issuers authorization endpoint and gets the access token',
|
||||
invoke: {
|
||||
src: 'invokeAuthorization',
|
||||
onDone: {
|
||||
actions: ['setTokenResponse', 'getKeyPairFromStore', 'loadKeyPair'],
|
||||
target: 'checkKeyPair',
|
||||
},
|
||||
onError: {
|
||||
actions: [() => console.log('error in invokeAuth - ', event.data)],
|
||||
target: 'downloadCredentials',
|
||||
},
|
||||
},
|
||||
},
|
||||
checkKeyPair: {
|
||||
description: 'checks whether key pair is generated',
|
||||
entry: [
|
||||
context =>
|
||||
log(
|
||||
'Reached CheckKeyPair context -> ',
|
||||
JSON.stringify(context, null, 4),
|
||||
),
|
||||
send('CHECK_KEY_PAIR'),
|
||||
],
|
||||
on: {
|
||||
CHECK_KEY_PAIR: [
|
||||
{
|
||||
cond: 'hasKeyPair',
|
||||
target: 'downloadCredentials',
|
||||
},
|
||||
{
|
||||
target: 'generateKeyPair',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
generateKeyPair: {
|
||||
description:
|
||||
'if keypair is not generated, new one is created and stored',
|
||||
invoke: {
|
||||
src: 'generateKeyPair',
|
||||
onDone: [
|
||||
{
|
||||
actions: ['setPublicKey', 'setPrivateKey', 'storeKeyPair'],
|
||||
target: 'downloadCredentials',
|
||||
},
|
||||
{
|
||||
actions: ['setPublicKey', 'storeKeyPair'],
|
||||
cond: 'isCustomSecureKeystore',
|
||||
target: 'downloadCredentials',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
downloadCredentials: {
|
||||
description: 'credential is downloaded from the selected issuer',
|
||||
invoke: {
|
||||
src: 'downloadCredential',
|
||||
onDone: {
|
||||
actions: 'setVerifiableCredential',
|
||||
target: 'verifyingCredential',
|
||||
},
|
||||
onError: {
|
||||
actions: () => console.log('in error of downloadCredential'),
|
||||
},
|
||||
},
|
||||
on: {
|
||||
CANCEL: {
|
||||
target: 'selectingIssuer',
|
||||
},
|
||||
},
|
||||
},
|
||||
verifyingCredential: {
|
||||
description:
|
||||
'once the credential is downloaded, it is verified before saving',
|
||||
invoke: {
|
||||
src: 'verifyCredential',
|
||||
onDone: [
|
||||
{
|
||||
target: 'storing',
|
||||
},
|
||||
],
|
||||
onError: [
|
||||
{
|
||||
actions: log((_, event) => (event.data as Error).message),
|
||||
//TODO: Move to state according to the required flow when verification of VC fails
|
||||
target: 'idle',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
storing: {
|
||||
description: 'all the verified credential is stored.',
|
||||
entry: [
|
||||
'storeVerifiableCredentialMeta',
|
||||
'storeVerifiableCredentialData',
|
||||
'storeVcsContext',
|
||||
'storeVcMetaContext',
|
||||
'logDownloaded',
|
||||
],
|
||||
},
|
||||
idle: {
|
||||
on: {
|
||||
COMPLETED: {
|
||||
target: 'done',
|
||||
},
|
||||
CANCEL: {
|
||||
target: 'selectingIssuer',
|
||||
},
|
||||
},
|
||||
},
|
||||
done: {
|
||||
entry: () => console.log('Reached done'),
|
||||
type: 'final',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
actions: {
|
||||
setIssuers: model.assign({
|
||||
issuers: (_, event) => event.data,
|
||||
loadingReason: null,
|
||||
}),
|
||||
|
||||
setError: model.assign({
|
||||
errorMessage: (_, event) => {
|
||||
console.log('Error while fetching issuers ', event.data.message);
|
||||
return event.data.message === 'Network request failed'
|
||||
? 'noInternetConnection'
|
||||
: 'generic';
|
||||
},
|
||||
loadingReason: null,
|
||||
}),
|
||||
|
||||
resetError: model.assign({
|
||||
errorMessage: '',
|
||||
}),
|
||||
|
||||
loadKeyPair: assign({
|
||||
publicKey: (_, event) => event.publicKey,
|
||||
privateKey: (context, event) =>
|
||||
event.privateKey ? event.privateKey : context.privateKey,
|
||||
}),
|
||||
getKeyPairFromStore: send(StoreEvents.GET(Issuers_Key_Ref), {
|
||||
to: context => context.serviceRefs.store,
|
||||
}),
|
||||
storeKeyPair: send(
|
||||
(_, event) => {
|
||||
return StoreEvents.SET(Issuers_Key_Ref, {
|
||||
publicKey: (event.data as KeyPair).public + ``,
|
||||
privateKey: (event.data as KeyPair).private + ``,
|
||||
});
|
||||
},
|
||||
{
|
||||
to: context => context.serviceRefs.store,
|
||||
},
|
||||
),
|
||||
|
||||
storeVerifiableCredentialMeta: send(
|
||||
context => {
|
||||
const [issuer, protocol, id] =
|
||||
context.verifiableCredential?.identifier.split(':');
|
||||
return StoreEvents.PREPEND(
|
||||
MY_VCS_STORE_KEY,
|
||||
VCMetadata.fromVC({
|
||||
id: id ? id : null,
|
||||
issuer: issuer,
|
||||
protocol: protocol,
|
||||
}),
|
||||
);
|
||||
},
|
||||
{
|
||||
to: context => context.serviceRefs.store,
|
||||
},
|
||||
),
|
||||
|
||||
storeVerifiableCredentialData: send(
|
||||
context => {
|
||||
const [issuer, protocol, id] =
|
||||
context.verifiableCredential?.identifier.split(':');
|
||||
return StoreEvents.SET(
|
||||
VCMetadata.fromVC({
|
||||
id: id ? id : null,
|
||||
issuer: issuer,
|
||||
protocol: protocol,
|
||||
}).getVcKey(),
|
||||
context.verifiableCredential,
|
||||
);
|
||||
},
|
||||
{
|
||||
to: context => context.serviceRefs.store,
|
||||
},
|
||||
),
|
||||
|
||||
storeVcMetaContext: send(
|
||||
context => {
|
||||
const [issuer, protocol, id] =
|
||||
context.verifiableCredential?.identifier.split(':');
|
||||
|
||||
return {
|
||||
type: 'VC_ADDED',
|
||||
vcMetadata: VCMetadata.fromVC({
|
||||
id: id ? id : null,
|
||||
issuer: issuer,
|
||||
protocol: protocol,
|
||||
}),
|
||||
};
|
||||
},
|
||||
{
|
||||
to: context => context.serviceRefs.vc,
|
||||
},
|
||||
),
|
||||
|
||||
storeVcsContext: send(
|
||||
context => {
|
||||
const [issuer, protocol, id] =
|
||||
context.verifiableCredential?.identifier.split(':');
|
||||
return {
|
||||
type: 'VC_DOWNLOADED_FROM_OPENID4VCI',
|
||||
vcMetadata: VCMetadata.fromVC({
|
||||
id: id ? id : null,
|
||||
issuer: issuer,
|
||||
protocol: protocol,
|
||||
}),
|
||||
vc: context.verifiableCredential,
|
||||
};
|
||||
},
|
||||
{
|
||||
to: context => context.serviceRefs.vc,
|
||||
},
|
||||
),
|
||||
|
||||
setSelectedIssuers: model.assign({
|
||||
selectedIssuer: (_, event) => event.data,
|
||||
}),
|
||||
setTokenResponse: model.assign({
|
||||
tokenResponse: (_, event) => event.data,
|
||||
loadingReason: 'settingUp',
|
||||
}),
|
||||
setVerifiableCredential: model.assign({
|
||||
verifiableCredential: (_, event) => {
|
||||
return event.data;
|
||||
},
|
||||
}),
|
||||
setPublicKey: assign({
|
||||
publicKey: (context, event) => {
|
||||
if (!isCustomSecureKeystore()) {
|
||||
return (event.data as KeyPair).public;
|
||||
}
|
||||
return event.data as string;
|
||||
},
|
||||
loadingReason: 'downloadingCredentials',
|
||||
}),
|
||||
|
||||
setPrivateKey: assign({
|
||||
privateKey: (context, event) => (event.data as KeyPair).private,
|
||||
}),
|
||||
|
||||
logDownloaded: send(
|
||||
context => {
|
||||
const [issuer, protocol, id] =
|
||||
context.verifiableCredential?.identifier.split(':');
|
||||
|
||||
return ActivityLogEvents.LOG_ACTIVITY({
|
||||
_vcKey: id,
|
||||
type: 'VC_DOWNLOADED',
|
||||
timestamp: Date.now(),
|
||||
deviceName: '',
|
||||
vcLabel: '',
|
||||
});
|
||||
},
|
||||
{
|
||||
to: context => context.serviceRefs.activityLog,
|
||||
},
|
||||
),
|
||||
},
|
||||
services: {
|
||||
downloadIssuersList: async () => {
|
||||
const defaultIssuer = {
|
||||
id: 'UIN, VID, AID',
|
||||
displayName: 'UIN, VID, AID',
|
||||
logoUrl: Theme.DigitIcon,
|
||||
};
|
||||
|
||||
const response = await request('GET', '/residentmobileapp/issuers');
|
||||
return [defaultIssuer, ...response.response.issuers];
|
||||
},
|
||||
downloadIssuerConfig: async (_, event) => {
|
||||
const response = await request(
|
||||
'GET',
|
||||
`/residentmobileapp/issuers/${event.id}`,
|
||||
);
|
||||
return response.response;
|
||||
},
|
||||
downloadCredential: async context => {
|
||||
const body = await getBody(context);
|
||||
const response = await fetch(
|
||||
'https://api-internal.dev1.mosip.net/v1/esignet/vci/credential',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: 'Bearer ' + context.tokenResponse?.accessToken,
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
},
|
||||
);
|
||||
let credential = await response.json();
|
||||
credential = updateCredentialInformation(context, credential);
|
||||
return credential;
|
||||
},
|
||||
invokeAuthorization: async context => {
|
||||
const response = await authorize(context.selectedIssuer);
|
||||
return response;
|
||||
},
|
||||
generateKeyPair: async context => {
|
||||
if (!isCustomSecureKeystore()) {
|
||||
return await generateKeys();
|
||||
}
|
||||
const isBiometricsEnabled = SecureKeystore.hasBiometricsEnabled();
|
||||
return SecureKeystore.generateKeyPair(
|
||||
Issuers_Key_Ref,
|
||||
isBiometricsEnabled,
|
||||
0,
|
||||
);
|
||||
return context;
|
||||
},
|
||||
verifyCredential: async context => {
|
||||
return verifyCredential(context.verifiableCredential?.credential);
|
||||
},
|
||||
},
|
||||
guards: {
|
||||
hasKeyPair: context => {
|
||||
return context.publicKey != null;
|
||||
},
|
||||
isCustomSecureKeystore: () => isCustomSecureKeystore(),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
type State = StateFrom<typeof IssuersMachine>;
|
||||
|
||||
export function selectIssuers(state: State) {
|
||||
return state.context.issuers;
|
||||
}
|
||||
|
||||
export function selectErrorMessage(state: State) {
|
||||
return state.context.errorMessage;
|
||||
}
|
||||
|
||||
export function selectLoadingReason(state: State) {
|
||||
return state.context.loadingReason;
|
||||
}
|
||||
|
||||
export function selectIsDownloadCredentials(state: State) {
|
||||
return state.matches('downloadCredentials');
|
||||
}
|
||||
|
||||
export function selectIsDone(state: State) {
|
||||
return state.matches('done');
|
||||
}
|
||||
|
||||
export function selectIsIdle(state: State) {
|
||||
return state.matches('idle');
|
||||
}
|
||||
|
||||
export function selectStoring(state: State) {
|
||||
return state.matches('storing');
|
||||
}
|
||||
|
||||
interface issuerType {
|
||||
id: string;
|
||||
displayName: string;
|
||||
logoUrl: string;
|
||||
}
|
||||
|
||||
const updateCredentialInformation = (context, credential) => {
|
||||
credential.identifier = getIdentifier(context, credential);
|
||||
credential.generatedOn = new Date();
|
||||
credential.issuerLogo = context.selectedIssuer.logoUrl;
|
||||
console.log(
|
||||
'Response from downloadCredential',
|
||||
JSON.stringify(credential, null, 4),
|
||||
);
|
||||
return credential;
|
||||
};
|
||||
108
machines/issuersMachine.typegen.ts
Normal file
108
machines/issuersMachine.typegen.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
// This file was automatically generated. Edits will be overwritten
|
||||
|
||||
export interface Typegen0 {
|
||||
'@@xstate/typegen': true;
|
||||
internalEvents: {
|
||||
'done.invoke.issuersMachine.displayIssuers:invocation[0]': {
|
||||
type: 'done.invoke.issuersMachine.displayIssuers:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'done.invoke.issuersMachine.downloadCredentials:invocation[0]': {
|
||||
type: 'done.invoke.issuersMachine.downloadCredentials:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'done.invoke.issuersMachine.downloadIssuerConfig:invocation[0]': {
|
||||
type: 'done.invoke.issuersMachine.downloadIssuerConfig:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'done.invoke.issuersMachine.generateKeyPair:invocation[0]': {
|
||||
type: 'done.invoke.issuersMachine.generateKeyPair:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'done.invoke.issuersMachine.performAuthorization:invocation[0]': {
|
||||
type: 'done.invoke.issuersMachine.performAuthorization:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'done.invoke.issuersMachine.verifyingCredential:invocation[0]': {
|
||||
type: 'done.invoke.issuersMachine.verifyingCredential:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'error.platform.issuersMachine.displayIssuers:invocation[0]': {
|
||||
type: 'error.platform.issuersMachine.displayIssuers:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'error.platform.issuersMachine.performAuthorization:invocation[0]': {
|
||||
type: 'error.platform.issuersMachine.performAuthorization:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'xstate.init': {type: 'xstate.init'};
|
||||
};
|
||||
invokeSrcNameMap: {
|
||||
downloadCredential: 'done.invoke.issuersMachine.downloadCredentials:invocation[0]';
|
||||
downloadIssuerConfig: 'done.invoke.issuersMachine.downloadIssuerConfig:invocation[0]';
|
||||
downloadIssuersList: 'done.invoke.issuersMachine.displayIssuers:invocation[0]';
|
||||
generateKeyPair: 'done.invoke.issuersMachine.generateKeyPair:invocation[0]';
|
||||
invokeAuthorization: 'done.invoke.issuersMachine.performAuthorization:invocation[0]';
|
||||
verifyCredential: 'done.invoke.issuersMachine.verifyingCredential:invocation[0]';
|
||||
};
|
||||
missingImplementations: {
|
||||
actions: never;
|
||||
delays: never;
|
||||
guards: never;
|
||||
services: never;
|
||||
};
|
||||
eventsCausingActions: {
|
||||
getKeyPairFromStore: 'done.invoke.issuersMachine.performAuthorization:invocation[0]';
|
||||
loadKeyPair: 'done.invoke.issuersMachine.performAuthorization:invocation[0]';
|
||||
logDownloaded: 'done.invoke.issuersMachine.verifyingCredential:invocation[0]';
|
||||
resetError: 'RESET_ERROR' | 'TRY_AGAIN';
|
||||
setError: 'error.platform.issuersMachine.displayIssuers:invocation[0]';
|
||||
setIssuers: 'done.invoke.issuersMachine.displayIssuers:invocation[0]';
|
||||
setPrivateKey: 'done.invoke.issuersMachine.generateKeyPair:invocation[0]';
|
||||
setPublicKey: 'done.invoke.issuersMachine.generateKeyPair:invocation[0]';
|
||||
setSelectedIssuers: 'done.invoke.issuersMachine.downloadIssuerConfig:invocation[0]';
|
||||
setTokenResponse: 'done.invoke.issuersMachine.performAuthorization:invocation[0]';
|
||||
setVerifiableCredential: 'done.invoke.issuersMachine.downloadCredentials:invocation[0]';
|
||||
storeKeyPair: 'done.invoke.issuersMachine.generateKeyPair:invocation[0]';
|
||||
storeVcMetaContext: 'done.invoke.issuersMachine.verifyingCredential:invocation[0]';
|
||||
storeVcsContext: 'done.invoke.issuersMachine.verifyingCredential:invocation[0]';
|
||||
storeVerifiableCredentialData: 'done.invoke.issuersMachine.verifyingCredential:invocation[0]';
|
||||
storeVerifiableCredentialMeta: 'done.invoke.issuersMachine.verifyingCredential:invocation[0]';
|
||||
};
|
||||
eventsCausingDelays: {};
|
||||
eventsCausingGuards: {
|
||||
hasKeyPair: 'CHECK_KEY_PAIR';
|
||||
isCustomSecureKeystore: 'done.invoke.issuersMachine.generateKeyPair:invocation[0]';
|
||||
};
|
||||
eventsCausingServices: {
|
||||
downloadCredential:
|
||||
| 'CHECK_KEY_PAIR'
|
||||
| 'done.invoke.issuersMachine.generateKeyPair:invocation[0]'
|
||||
| 'error.platform.issuersMachine.performAuthorization:invocation[0]';
|
||||
downloadIssuerConfig: 'SELECTED_ISSUER';
|
||||
downloadIssuersList: 'TRY_AGAIN' | 'xstate.init';
|
||||
generateKeyPair: 'CHECK_KEY_PAIR';
|
||||
invokeAuthorization: 'done.invoke.issuersMachine.downloadIssuerConfig:invocation[0]';
|
||||
verifyCredential: 'done.invoke.issuersMachine.downloadCredentials:invocation[0]';
|
||||
};
|
||||
matchesStates:
|
||||
| 'checkKeyPair'
|
||||
| 'displayIssuers'
|
||||
| 'done'
|
||||
| 'downloadCredentials'
|
||||
| 'downloadIssuerConfig'
|
||||
| 'error'
|
||||
| 'generateKeyPair'
|
||||
| 'idle'
|
||||
| 'performAuthorization'
|
||||
| 'selectingIssuer'
|
||||
| 'storing'
|
||||
| 'verifyingCredential';
|
||||
tags: never;
|
||||
}
|
||||
@@ -4,9 +4,11 @@ import {StoreEvents} from './store';
|
||||
import {VC} from '../types/vc';
|
||||
import {AppServices} from '../shared/GlobalContext';
|
||||
import {log, respond} from 'xstate/lib/actions';
|
||||
import {VcItemEvents} from './vcItem';
|
||||
import {ExistingMosipVCItemEvents} from './VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine';
|
||||
import {MY_VCS_STORE_KEY, RECEIVED_VCS_STORE_KEY} from '../shared/constants';
|
||||
import {parseMetadatas, VCMetadata} from '../shared/VCMetadata';
|
||||
import {OpenId4VCIProtocol} from '../shared/openId4VCI/Utils';
|
||||
import {EsignetMosipVCItemEvents} from './VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine';
|
||||
|
||||
const model = createModel(
|
||||
{
|
||||
@@ -26,6 +28,10 @@ const model = createModel(
|
||||
VC_METADATA_UPDATED: (vcMetadata: VCMetadata) => ({vcMetadata}),
|
||||
VC_RECEIVED: (vcMetadata: VCMetadata) => ({vcMetadata}),
|
||||
VC_DOWNLOADED: (vc: VC) => ({vc}),
|
||||
VC_DOWNLOADED_FROM_OPENID4VCI: (vc: VC, vcMetadata: VCMetadata) => ({
|
||||
vc,
|
||||
vcMetadata,
|
||||
}),
|
||||
VC_UPDATE: (vc: VC) => ({vc}),
|
||||
REFRESH_MY_VCS: () => ({}),
|
||||
REFRESH_MY_VCS_TWO: (vc: VC) => ({vc}),
|
||||
@@ -146,6 +152,9 @@ export const vcMachine =
|
||||
VC_DOWNLOADED: {
|
||||
actions: 'setDownloadedVc',
|
||||
},
|
||||
VC_DOWNLOADED_FROM_OPENID4VCI: {
|
||||
actions: 'setDownloadedVCFromOpenId4VCI',
|
||||
},
|
||||
VC_UPDATE: {
|
||||
actions: 'setVcUpdate',
|
||||
},
|
||||
@@ -171,7 +180,10 @@ export const vcMachine =
|
||||
|
||||
getVcItemResponse: respond((context, event) => {
|
||||
const vc = context.vcs[event.vcMetadata?.getVcKey()];
|
||||
return VcItemEvents.GET_VC_RESPONSE(vc);
|
||||
if (event.protocol === OpenId4VCIProtocol) {
|
||||
return EsignetMosipVCItemEvents.GET_VC_RESPONSE(vc);
|
||||
}
|
||||
return ExistingMosipVCItemEvents.GET_VC_RESPONSE(vc);
|
||||
}),
|
||||
|
||||
loadMyVcs: send(StoreEvents.GET(MY_VCS_STORE_KEY), {
|
||||
@@ -199,6 +211,10 @@ export const vcMachine =
|
||||
context.vcs[vcUniqueId] = event.vc;
|
||||
},
|
||||
|
||||
setDownloadedVCFromOpenId4VCI: (context, event) => {
|
||||
if (event.vc) context.vcs[event.vcMetadata.getVcKey()] = event.vc;
|
||||
},
|
||||
|
||||
setVcUpdate: (context, event) => {
|
||||
Object.keys(context.vcs).map(vcUniqueId => {
|
||||
const eventVCMetadata = VCMetadata.fromVC(event.vc);
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
|
||||
export interface Typegen0 {
|
||||
'@@xstate/typegen': true;
|
||||
'internalEvents': {
|
||||
'xstate.init': { type: 'xstate.init' };
|
||||
internalEvents: {
|
||||
'xstate.init': {type: 'xstate.init'};
|
||||
};
|
||||
'invokeSrcNameMap': {};
|
||||
'missingImplementations': {
|
||||
invokeSrcNameMap: {};
|
||||
missingImplementations: {
|
||||
actions: never;
|
||||
delays: never;
|
||||
guards: never;
|
||||
services: never;
|
||||
};
|
||||
'eventsCausingActions': {
|
||||
eventsCausingActions: {
|
||||
getReceivedVcsResponse: 'GET_RECEIVED_VCS';
|
||||
getVcItemResponse: 'GET_VC_ITEM';
|
||||
loadMyVcs: 'REFRESH_MY_VCS' | 'xstate.init';
|
||||
@@ -21,19 +21,20 @@ export interface Typegen0 {
|
||||
prependToMyVcs: 'VC_ADDED';
|
||||
prependToReceivedVcs: 'VC_RECEIVED';
|
||||
removeVcFromMyVcs: 'REMOVE_VC_FROM_CONTEXT';
|
||||
setDownloadedVCFromOpenId4VCI: 'VC_DOWNLOADED_FROM_OPENID4VCI';
|
||||
setDownloadedVc: 'VC_DOWNLOADED';
|
||||
setMyVcs: 'STORE_RESPONSE';
|
||||
setReceivedVcs: 'STORE_RESPONSE';
|
||||
setUpdateVc: 'VC_UPDATED';
|
||||
setUpdatedVcMetadatas: 'VC_METADATA_UPDATED';
|
||||
setVcUpdate: 'VC_UPDATE';
|
||||
updateMyVcs: 'VC_UPDATED';
|
||||
updateMyVcs: 'VC_METADATA_UPDATED';
|
||||
};
|
||||
'eventsCausingDelays': {};
|
||||
'eventsCausingGuards': {
|
||||
eventsCausingDelays: {};
|
||||
eventsCausingGuards: {
|
||||
hasExistingReceivedVc: 'VC_RECEIVED';
|
||||
};
|
||||
'eventsCausingServices': {};
|
||||
'matchesStates':
|
||||
eventsCausingServices: {};
|
||||
matchesStates:
|
||||
| 'init'
|
||||
| 'init.myVcs'
|
||||
| 'init.receivedVcs'
|
||||
@@ -54,5 +55,5 @@ export interface Typegen0 {
|
||||
receivedVcs?: 'idle' | 'refreshing';
|
||||
};
|
||||
};
|
||||
'tags': never;
|
||||
tags: never;
|
||||
}
|
||||
|
||||
3305
package-lock.json
generated
3305
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -44,14 +44,17 @@
|
||||
"expo-updates": "~0.16.4",
|
||||
"i18next": "^21.6.16",
|
||||
"iso-639-3": "^3.0.1",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"mosip-inji-face-sdk": "^0.1.12",
|
||||
"node-forge": "^1.3.1",
|
||||
"node-jose": "^2.2.0",
|
||||
"patch-package": "^6.5.1",
|
||||
"postinstall-postinstall": "^2.1.0",
|
||||
"react": "18.2.0",
|
||||
"react-i18next": "^11.16.6",
|
||||
"react-native": "0.71.8",
|
||||
"react-native-animated-pagination-dot": "^0.4.0",
|
||||
"react-native-app-auth": "^7.0.0",
|
||||
"react-native-app-intro-slider": "^4.0.4",
|
||||
"react-native-argon2": "^2.0.1",
|
||||
"react-native-biometrics-changed": "^1.1.8",
|
||||
@@ -79,6 +82,7 @@
|
||||
"react-native-secure-keystore": "github:mosip/secure-keystore#v0.1.1",
|
||||
"react-native-securerandom": "^1.0.1",
|
||||
"react-native-simple-markdown": "^1.1.0",
|
||||
"react-native-spinkit": "^1.5.1",
|
||||
"react-native-svg": "13.4.0",
|
||||
"react-native-swipe-gestures": "^1.0.5",
|
||||
"react-native-tuvali": "github:mosip/tuvali#v0.4.4",
|
||||
|
||||
@@ -4,24 +4,20 @@ import {
|
||||
BottomTabScreenProps,
|
||||
} from '@react-navigation/bottom-tabs';
|
||||
import {Image} from 'react-native';
|
||||
import {HomeScreen} from '../screens/Home/HomeScreen';
|
||||
import {RootStackParamList} from './index';
|
||||
import {ScanLayout} from '../screens/Scan/ScanLayout';
|
||||
import {HistoryScreen} from '../screens/History/HistoryScreen';
|
||||
import i18n from '../i18n';
|
||||
import {BOTTOM_TAB_ROUTES} from './routesConstants';
|
||||
import {HomeScreenLayout} from '../screens/HomeScreenLayout';
|
||||
|
||||
const home: TabScreen = {
|
||||
name: BOTTOM_TAB_ROUTES.home,
|
||||
component: HomeScreen,
|
||||
component: HomeScreenLayout,
|
||||
icon: 'home',
|
||||
options: {
|
||||
headerTitle: '',
|
||||
headerLeft: () =>
|
||||
React.createElement(Image, {
|
||||
source: require('../assets/inji-home-logo.png'),
|
||||
style: {width: 124, height: 27, resizeMode: 'contain'},
|
||||
}),
|
||||
headerShown: false,
|
||||
},
|
||||
};
|
||||
export const scan: TabScreen = {
|
||||
|
||||
@@ -1,21 +1,62 @@
|
||||
import React from 'react';
|
||||
import { Tab } from 'react-native-elements';
|
||||
import { Column, Text } from '../../components/ui';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { HomeRouteProps } from '../../routes/main';
|
||||
import { MyVcsTab } from './MyVcsTab';
|
||||
import { ReceivedVcsTab } from './ReceivedVcsTab';
|
||||
import { ViewVcModal } from './ViewVcModal';
|
||||
import { useHomeScreen } from './HomeScreenController';
|
||||
import { TabRef } from './HomeScreenMachine';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import { vcItemMachine } from '../../machines/vcItem';
|
||||
import React, {useEffect} from 'react';
|
||||
import {Icon, Tab} from 'react-native-elements';
|
||||
import {Button, Column, Text} from '../../components/ui';
|
||||
import {Theme} from '../../components/ui/styleUtils';
|
||||
import {HomeRouteProps} from '../../routes/main';
|
||||
import {MyVcsTab} from './MyVcsTab';
|
||||
import {ReceivedVcsTab} from './ReceivedVcsTab';
|
||||
import {ViewVcModal} from './ViewVcModal';
|
||||
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 {isOpenId4VCIEnabled} from '../../shared/openId4VCI/Utils';
|
||||
import LinearGradient from 'react-native-linear-gradient';
|
||||
import {EsignetMosipVCItemMachine} from '../../machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine';
|
||||
|
||||
export const HomeScreen: React.FC<HomeRouteProps> = (props) => {
|
||||
const { t } = useTranslation('HomeScreen');
|
||||
export const HomeScreen: React.FC<HomeRouteProps> = props => {
|
||||
const {t} = useTranslation('HomeScreen');
|
||||
const controller = useHomeScreen(props);
|
||||
|
||||
useEffect(() => {
|
||||
if (controller.IssuersService) {
|
||||
navigateToIssuers();
|
||||
}
|
||||
}, [controller.IssuersService]);
|
||||
|
||||
const navigateToIssuers = () => {
|
||||
props.navigation.navigate('IssuersScreen', {
|
||||
service: controller.IssuersService,
|
||||
});
|
||||
};
|
||||
|
||||
const DownloadFABIcon: React.FC = () => {
|
||||
const plusIcon = (
|
||||
<Icon
|
||||
name={'plus'}
|
||||
type={'entypo'}
|
||||
size={36}
|
||||
color={Theme.Colors.whiteText}
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<LinearGradient
|
||||
colors={Theme.Colors.gradientBtn}
|
||||
style={Theme.Styles.downloadFabIcon}>
|
||||
<Button
|
||||
testID="downloadIcon"
|
||||
icon={plusIcon}
|
||||
onPress={() => {
|
||||
controller.GOTO_ISSUERS();
|
||||
}}
|
||||
type={'clearAddIdBtnBg'}
|
||||
fill
|
||||
/>
|
||||
</LinearGradient>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Column fill backgroundColor={Theme.Colors.lightGreyBackgroundColor}>
|
||||
@@ -34,6 +75,7 @@ export const HomeScreen: React.FC<HomeRouteProps> = (props) => {
|
||||
</Column>
|
||||
)}
|
||||
</Column>
|
||||
{isOpenId4VCIEnabled() && <DownloadFABIcon />}
|
||||
{controller.selectedVc && (
|
||||
<ViewVcModal
|
||||
isVisible={controller.isViewingVc}
|
||||
@@ -65,5 +107,7 @@ function TabItem(title: string) {
|
||||
export interface HomeScreenTabProps {
|
||||
isVisible: boolean;
|
||||
service: TabRef;
|
||||
vcItemActor: ActorRefFrom<typeof vcItemMachine>;
|
||||
vcItemActor:
|
||||
| ActorRefFrom<typeof ExistingMosipVCItemMachine>
|
||||
| ActorRefFrom<typeof EsignetMosipVCItemMachine>;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useInterpret, useSelector } from '@xstate/react';
|
||||
import { useContext, useEffect, useRef } from 'react';
|
||||
import { HomeRouteProps } from '../../routes/main';
|
||||
import { GlobalContext } from '../../shared/GlobalContext';
|
||||
import {useInterpret, useSelector} from '@xstate/react';
|
||||
import {useContext, useEffect, useRef} from 'react';
|
||||
import {HomeRouteProps} from '../../routes/main';
|
||||
import {GlobalContext} from '../../shared/GlobalContext';
|
||||
import {
|
||||
HomeScreenEvents,
|
||||
HomeScreenMachine,
|
||||
@@ -10,16 +10,17 @@ import {
|
||||
selectTabRefs,
|
||||
selectTabsLoaded,
|
||||
selectViewingVc,
|
||||
selectIssuersMachine,
|
||||
} from './HomeScreenMachine';
|
||||
import { VcEvents } from '../../machines/vc';
|
||||
import {VcEvents} from '../../machines/vc';
|
||||
|
||||
export function useHomeScreen(props: HomeRouteProps) {
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const machine = useRef(
|
||||
HomeScreenMachine.withContext({
|
||||
...HomeScreenMachine.context,
|
||||
serviceRefs: appService.getSnapshot().context.serviceRefs,
|
||||
})
|
||||
}),
|
||||
);
|
||||
const service = useInterpret(machine.current);
|
||||
const vcService = appService.children.get('vc');
|
||||
@@ -40,6 +41,9 @@ export function useHomeScreen(props: HomeRouteProps) {
|
||||
isViewingVc: useSelector(service, selectViewingVc),
|
||||
haveTabsLoaded: useSelector(service, selectTabsLoaded),
|
||||
|
||||
IssuersService: useSelector(service, selectIssuersMachine),
|
||||
GOTO_ISSUERS: () => service.send(HomeScreenEvents.GOTO_ISSUERS()),
|
||||
|
||||
SELECT_TAB,
|
||||
DISMISS_MODAL: () => service.send(HomeScreenEvents.DISMISS_MODAL()),
|
||||
REVOKE: () => {
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
import {
|
||||
ActorRefFrom,
|
||||
assign,
|
||||
EventFrom,
|
||||
send,
|
||||
spawn,
|
||||
StateFrom,
|
||||
} from 'xstate';
|
||||
import { createModel } from 'xstate/lib/model';
|
||||
import { vcItemMachine } from '../../machines/vcItem';
|
||||
import { AppServices } from '../../shared/GlobalContext';
|
||||
import { createMyVcsTabMachine, MyVcsTabMachine } from './MyVcsTabMachine';
|
||||
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';
|
||||
|
||||
const model = createModel(
|
||||
{
|
||||
@@ -22,7 +17,9 @@ const model = createModel(
|
||||
myVcs: {} as ActorRefFrom<typeof MyVcsTabMachine>,
|
||||
receivedVcs: {} as ActorRefFrom<typeof ReceivedVcsTabMachine>,
|
||||
},
|
||||
selectedVc: null as ActorRefFrom<typeof vcItemMachine>,
|
||||
selectedVc: null as
|
||||
| ActorRefFrom<typeof ExistingMosipVCItemMachine>
|
||||
| ActorRefFrom<typeof EsignetMosipVCItemMachine>,
|
||||
activeTab: 0,
|
||||
},
|
||||
{
|
||||
@@ -30,12 +27,18 @@ const model = createModel(
|
||||
SELECT_MY_VCS: () => ({}),
|
||||
SELECT_RECEIVED_VCS: () => ({}),
|
||||
SELECT_HISTORY: () => ({}),
|
||||
VIEW_VC: (vcItemActor: ActorRefFrom<typeof vcItemMachine>) => ({
|
||||
VIEW_VC: (
|
||||
vcItemActor:
|
||||
| ActorRefFrom<typeof ExistingMosipVCItemMachine>
|
||||
| ActorRefFrom<typeof EsignetMosipVCItemMachine>,
|
||||
) => ({
|
||||
vcItemActor,
|
||||
}),
|
||||
DISMISS_MODAL: () => ({}),
|
||||
GOTO_ISSUERS: () => ({}),
|
||||
DOWNLOAD_ID: () => ({}),
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const MY_VCS_TAB_REF_ID = 'myVcsTab';
|
||||
@@ -66,6 +69,7 @@ export const HomeScreenMachine = model.createMachine(
|
||||
SELECT_MY_VCS: '.myVcs',
|
||||
SELECT_RECEIVED_VCS: '.receivedVcs',
|
||||
SELECT_HISTORY: '.history',
|
||||
GOTO_ISSUERS: '.gotoIssuers',
|
||||
},
|
||||
states: {
|
||||
init: {
|
||||
@@ -80,7 +84,7 @@ export const HomeScreenMachine = model.createMachine(
|
||||
DISMISS_MODAL: {
|
||||
actions: [
|
||||
send('DISMISS', {
|
||||
to: (context) => context.tabRefs.myVcs,
|
||||
to: context => context.tabRefs.myVcs,
|
||||
}),
|
||||
],
|
||||
},
|
||||
@@ -92,7 +96,7 @@ export const HomeScreenMachine = model.createMachine(
|
||||
DISMISS_MODAL: {
|
||||
actions: [
|
||||
send('DISMISS', {
|
||||
to: (context) => context.tabRefs.receivedVcs,
|
||||
to: context => context.tabRefs.receivedVcs,
|
||||
}),
|
||||
],
|
||||
},
|
||||
@@ -101,6 +105,29 @@ export const HomeScreenMachine = model.createMachine(
|
||||
history: {
|
||||
entry: [setActiveTab(2)],
|
||||
},
|
||||
gotoIssuers: {
|
||||
invoke: {
|
||||
id: 'issuersMachine',
|
||||
src: IssuersMachine,
|
||||
data: context => ({
|
||||
...IssuersMachine.context,
|
||||
serviceRefs: context.serviceRefs, // the value you want to pass to child machine
|
||||
}),
|
||||
onDone: 'idle',
|
||||
},
|
||||
on: {
|
||||
DOWNLOAD_ID: {
|
||||
actions: 'sendAddEvent',
|
||||
target: 'idle',
|
||||
},
|
||||
GOTO_ISSUERS: 'gotoIssuers',
|
||||
},
|
||||
},
|
||||
idle: {
|
||||
on: {
|
||||
GOTO_ISSUERS: 'gotoIssuers',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
modals: {
|
||||
@@ -127,18 +154,22 @@ export const HomeScreenMachine = model.createMachine(
|
||||
{
|
||||
actions: {
|
||||
spawnTabActors: assign({
|
||||
tabRefs: (context) => ({
|
||||
tabRefs: context => ({
|
||||
myVcs: spawn(
|
||||
createMyVcsTabMachine(context.serviceRefs),
|
||||
MY_VCS_TAB_REF_ID
|
||||
MY_VCS_TAB_REF_ID,
|
||||
),
|
||||
receivedVcs: spawn(
|
||||
createReceivedVcsTabMachine(context.serviceRefs),
|
||||
RECEIVED_VCS_TAB_REF_ID
|
||||
RECEIVED_VCS_TAB_REF_ID,
|
||||
),
|
||||
}),
|
||||
}),
|
||||
|
||||
sendAddEvent: send('ADD_VC', {
|
||||
to: context => context.tabRefs.myVcs,
|
||||
}),
|
||||
|
||||
setSelectedVc: model.assign({
|
||||
selectedVc: (_, event) => event.vcItemActor,
|
||||
}),
|
||||
@@ -147,15 +178,19 @@ export const HomeScreenMachine = model.createMachine(
|
||||
selectedVc: null,
|
||||
}),
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
function setActiveTab(activeTab: number) {
|
||||
return model.assign({ activeTab });
|
||||
return model.assign({activeTab});
|
||||
}
|
||||
|
||||
type State = StateFrom<typeof HomeScreenMachine>;
|
||||
|
||||
export function selectIssuersMachine(state: State) {
|
||||
return state.children.issuersMachine as ActorRefFrom<typeof IssuersMachine>;
|
||||
}
|
||||
|
||||
export function selectTabRefs(state: State) {
|
||||
return state.context.tabRefs;
|
||||
}
|
||||
|
||||
@@ -2,39 +2,50 @@
|
||||
|
||||
export interface Typegen0 {
|
||||
'@@xstate/typegen': true;
|
||||
'internalEvents': {
|
||||
internalEvents: {
|
||||
'xstate.after(100)#HomeScreen.tabs.init': {
|
||||
type: 'xstate.after(100)#HomeScreen.tabs.init';
|
||||
};
|
||||
'xstate.init': { type: 'xstate.init' };
|
||||
'xstate.init': {type: 'xstate.init'};
|
||||
};
|
||||
'invokeSrcNameMap': {};
|
||||
'missingImplementations': {
|
||||
invokeSrcNameMap: {};
|
||||
missingImplementations: {
|
||||
actions: never;
|
||||
delays: never;
|
||||
guards: never;
|
||||
services: never;
|
||||
};
|
||||
'eventsCausingActions': {
|
||||
eventsCausingActions: {
|
||||
resetSelectedVc: 'DISMISS_MODAL' | 'xstate.init';
|
||||
sendAddEvent: 'DOWNLOAD_ID';
|
||||
setSelectedVc: 'VIEW_VC';
|
||||
spawnTabActors: 'xstate.init';
|
||||
};
|
||||
'eventsCausingDelays': {};
|
||||
'eventsCausingGuards': {};
|
||||
'eventsCausingServices': {};
|
||||
'matchesStates':
|
||||
eventsCausingDelays: {};
|
||||
eventsCausingGuards: {};
|
||||
eventsCausingServices: {
|
||||
issuersMachine: 'GOTO_ISSUERS';
|
||||
};
|
||||
matchesStates:
|
||||
| 'modals'
|
||||
| 'modals.none'
|
||||
| 'modals.viewingVc'
|
||||
| 'tabs'
|
||||
| 'tabs.gotoIssuers'
|
||||
| 'tabs.history'
|
||||
| 'tabs.idle'
|
||||
| 'tabs.init'
|
||||
| 'tabs.myVcs'
|
||||
| 'tabs.receivedVcs'
|
||||
| {
|
||||
modals?: 'none' | 'viewingVc';
|
||||
tabs?: 'history' | 'init' | 'myVcs' | 'receivedVcs';
|
||||
tabs?:
|
||||
| 'gotoIssuers'
|
||||
| 'history'
|
||||
| 'idle'
|
||||
| 'init'
|
||||
| 'myVcs'
|
||||
| 'receivedVcs';
|
||||
};
|
||||
'tags': never;
|
||||
tags: never;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import {Modal} from '../../../components/ui/Modal';
|
||||
import {Centered, Column, Text} from '../../../components/ui';
|
||||
import {ActivityLogText} from '../../../components/ActivityLogText';
|
||||
import {ActorRefFrom} from 'xstate';
|
||||
import {vcItemMachine} from '../../../machines/vcItem';
|
||||
import {ExistingMosipVCItemMachine} from '../../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine';
|
||||
import {useKebabPopUp} from '../../../components/KebabPopUpController';
|
||||
import {Theme} from '../../../components/ui/styleUtils';
|
||||
import {VCMetadata} from '../../../shared/VCMetadata';
|
||||
@@ -67,5 +67,5 @@ export interface HistoryTabProps {
|
||||
testID?: string;
|
||||
label: string;
|
||||
vcMetadata: VCMetadata;
|
||||
service: ActorRefFrom<typeof vcItemMachine>;
|
||||
service: ActorRefFrom<typeof ExistingMosipVCItemMachine>;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import {MessageOverlay} from '../../../components/MessageOverlay';
|
||||
import {useKebabPopUp} from '../../../components/KebabPopUpController';
|
||||
import {Dimensions} from 'react-native';
|
||||
import {ActorRefFrom} from 'xstate';
|
||||
import {vcItemMachine} from '../../../machines/vcItem';
|
||||
import {ExistingMosipVCItemMachine} from '../../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine';
|
||||
import testIDProps from '../../../shared/commonUtil';
|
||||
|
||||
export const WalletBinding: React.FC<WalletBindingProps> = props => {
|
||||
@@ -102,5 +102,5 @@ interface WalletBindingProps {
|
||||
label: string;
|
||||
content?: string;
|
||||
Icon?: string;
|
||||
service: ActorRefFrom<typeof vcItemMachine>;
|
||||
service: ActorRefFrom<typeof ExistingMosipVCItemMachine>;
|
||||
}
|
||||
|
||||
@@ -1,34 +1,33 @@
|
||||
import { useSelector, useInterpret } from '@xstate/react';
|
||||
import { useContext, useRef, useState } from 'react';
|
||||
import { GlobalContext } from '../../../shared/GlobalContext';
|
||||
import { selectMyVcsMetadata, VcEvents } from '../../../machines/vc';
|
||||
import {useSelector, useInterpret} from '@xstate/react';
|
||||
import {useContext, useRef, useState} from 'react';
|
||||
import {GlobalContext} from '../../../shared/GlobalContext';
|
||||
import {selectMyVcsMetadata, VcEvents} from '../../../machines/vc';
|
||||
import {
|
||||
createVcItemMachine,
|
||||
createExistingMosipVCItemMachine,
|
||||
isShowingBindingWarning,
|
||||
selectAcceptingBindingOtp,
|
||||
isWalletBindingInProgress,
|
||||
VcItemEvents,
|
||||
ExistingMosipVCItemEvents,
|
||||
selectIsAcceptingOtpInput,
|
||||
selectOtpError,
|
||||
selectShowWalletBindingError,
|
||||
selectWalletBindingError,
|
||||
} from '../../../machines/vcItem';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
} from '../../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import {ActorRefFrom} from 'xstate';
|
||||
|
||||
export function useWalletBinding(props) {
|
||||
const { t } = useTranslation('ProfileScreen');
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const {t} = useTranslation('ProfileScreen');
|
||||
const {appService} = useContext(GlobalContext);
|
||||
|
||||
const machine = useRef(
|
||||
createVcItemMachine(
|
||||
createExistingMosipVCItemMachine(
|
||||
appService.getSnapshot().context.serviceRefs,
|
||||
props.vcMetadata
|
||||
)
|
||||
props.vcMetadata,
|
||||
),
|
||||
);
|
||||
|
||||
const bindingService = useInterpret(machine.current, { devTools: __DEV__ });
|
||||
const bindingService = useInterpret(machine.current, {devTools: __DEV__});
|
||||
|
||||
const vcService = appService.children.get('vc');
|
||||
|
||||
@@ -52,7 +51,7 @@ export function useWalletBinding(props) {
|
||||
|
||||
const WalletBindingInProgress = useSelector(
|
||||
bindingService,
|
||||
isWalletBindingInProgress
|
||||
isWalletBindingInProgress,
|
||||
);
|
||||
|
||||
return {
|
||||
@@ -66,16 +65,16 @@ export function useWalletBinding(props) {
|
||||
isAcceptingOtpInput: useSelector(bindingService, selectIsAcceptingOtpInput),
|
||||
isAcceptingBindingOtp: useSelector(
|
||||
bindingService,
|
||||
selectAcceptingBindingOtp
|
||||
selectAcceptingBindingOtp,
|
||||
),
|
||||
isBindingError: useSelector(bindingService, selectShowWalletBindingError),
|
||||
walletBindingError: useSelector(bindingService, selectWalletBindingError),
|
||||
|
||||
DISMISS: () => bindingService.send(VcItemEvents.DISMISS()),
|
||||
DISMISS: () => bindingService.send(ExistingMosipVCItemEvents.DISMISS()),
|
||||
|
||||
CONFIRM: () => bindingService.send(VcItemEvents.CONFIRM()),
|
||||
CONFIRM: () => bindingService.send(ExistingMosipVCItemEvents.CONFIRM()),
|
||||
|
||||
CANCEL: () => bindingService.send(VcItemEvents.CANCEL()),
|
||||
CANCEL: () => bindingService.send(ExistingMosipVCItemEvents.CANCEL()),
|
||||
|
||||
REFRESH: () => vcService.send(VcEvents.REFRESH_MY_VCS()),
|
||||
setAuthenticating,
|
||||
|
||||
@@ -7,7 +7,7 @@ import {HomeScreenTabProps} from './HomeScreen';
|
||||
import {AddVcModal} from './MyVcs/AddVcModal';
|
||||
import {GetVcModal} from './MyVcs/GetVcModal';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {VcItem} from '../../components/VcItem';
|
||||
import {ExistingMosipVCItem} from '../../components/VC/ExistingMosipVCItem/ExistingMosipVCItem';
|
||||
import {GET_INDIVIDUAL_ID} from '../../shared/constants';
|
||||
import {
|
||||
ErrorMessageOverlay,
|
||||
@@ -15,6 +15,8 @@ import {
|
||||
} from '../../components/MessageOverlay';
|
||||
import {Icon} from 'react-native-elements';
|
||||
import {groupBy} from '../../shared/javascript';
|
||||
import {isOpenId4VCIEnabled} from '../../shared/openId4VCI/Utils';
|
||||
import {VcItemContainer} from '../../components/VC/VcItemContainer';
|
||||
|
||||
const pinIconProps = {iconName: 'pushpin', iconType: 'antdesign'};
|
||||
|
||||
@@ -87,7 +89,7 @@ export const MyVcsTab: React.FC<HomeScreenTabProps> = props => {
|
||||
{vcMetadataOrderedByPinStatus.map((vcMetadata, index) => {
|
||||
const iconProps = vcMetadata.isPinned ? pinIconProps : {};
|
||||
return (
|
||||
<VcItem
|
||||
<VcItemContainer
|
||||
{...iconProps}
|
||||
key={`${vcMetadata.getVcKey()}-${index}`}
|
||||
vcMetadata={vcMetadata}
|
||||
@@ -97,13 +99,15 @@ export const MyVcsTab: React.FC<HomeScreenTabProps> = props => {
|
||||
);
|
||||
})}
|
||||
</Column>
|
||||
<Button
|
||||
testID="downloadCard"
|
||||
type="gradient"
|
||||
disabled={controller.isRefreshingVcs}
|
||||
title={t('downloadCard')}
|
||||
onPress={controller.DOWNLOAD_ID}
|
||||
/>
|
||||
{!isOpenId4VCIEnabled() && (
|
||||
<Button
|
||||
testID="downloadCard"
|
||||
type="gradient"
|
||||
disabled={controller.isRefreshingVcs}
|
||||
title={t('downloadCard')}
|
||||
onPress={controller.DOWNLOAD_ID}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)}
|
||||
{controller.vcMetadatas.length === 0 && (
|
||||
@@ -125,13 +129,15 @@ export const MyVcsTab: React.FC<HomeScreenTabProps> = props => {
|
||||
margin="0 12 30 12">
|
||||
{t('generateVcDescription')}
|
||||
</Text>
|
||||
<Button
|
||||
testID="downloadCard"
|
||||
type="gradient"
|
||||
disabled={controller.isRefreshingVcs}
|
||||
title={t('downloadCard')}
|
||||
onPress={controller.DOWNLOAD_ID}
|
||||
/>
|
||||
{!isOpenId4VCIEnabled() && (
|
||||
<Button
|
||||
testID="downloadCard"
|
||||
type="gradient"
|
||||
disabled={controller.isRefreshingVcs}
|
||||
title={t('downloadCard')}
|
||||
onPress={controller.DOWNLOAD_ID}
|
||||
/>
|
||||
)}
|
||||
</Column>
|
||||
</React.Fragment>
|
||||
)}
|
||||
|
||||
@@ -10,8 +10,8 @@ import {
|
||||
import {
|
||||
selectWalletBindingError,
|
||||
selectShowWalletBindingError,
|
||||
} from '../../machines/vcItem';
|
||||
import {vcItemMachine} from '../../machines/vcItem';
|
||||
} from '../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine';
|
||||
import {ExistingMosipVCItemMachine} from '../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine';
|
||||
import {GlobalContext} from '../../shared/GlobalContext';
|
||||
import {HomeScreenTabProps} from './HomeScreen';
|
||||
import {
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
selectShowHardwareKeystoreNotExistsAlert,
|
||||
SettingsEvents,
|
||||
} from '../../machines/settings';
|
||||
import {EsignetMosipVCItemMachine} from '../../machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine';
|
||||
|
||||
export function useMyVcsTab(props: HomeScreenTabProps) {
|
||||
const service = props.service as ActorRefFrom<typeof MyVcsTabMachine>;
|
||||
@@ -64,7 +65,11 @@ export function useMyVcsTab(props: HomeScreenTabProps) {
|
||||
|
||||
REFRESH: () => vcService.send(VcEvents.REFRESH_MY_VCS()),
|
||||
|
||||
VIEW_VC: (vcRef: ActorRefFrom<typeof vcItemMachine>) => {
|
||||
VIEW_VC: (
|
||||
vcRef:
|
||||
| ActorRefFrom<typeof ExistingMosipVCItemMachine>
|
||||
| ActorRefFrom<typeof EsignetMosipVCItemMachine>,
|
||||
) => {
|
||||
return service.send(MyVcsTabEvents.VIEW_VC(vcRef));
|
||||
},
|
||||
|
||||
|
||||
@@ -9,13 +9,14 @@ import {
|
||||
import {createModel} from 'xstate/lib/model';
|
||||
import {StoreEvents, StoreResponseEvent} from '../../machines/store';
|
||||
import {VcEvents} from '../../machines/vc';
|
||||
import {vcItemMachine} from '../../machines/vcItem';
|
||||
import {ExistingMosipVCItemMachine} from '../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine';
|
||||
import {AppServices} from '../../shared/GlobalContext';
|
||||
import {MY_VCS_STORE_KEY} from '../../shared/constants';
|
||||
import {AddVcModalMachine} from './MyVcs/AddVcModalMachine';
|
||||
import {GetVcModalMachine} from './MyVcs/GetVcModalMachine';
|
||||
import Storage from '../../shared/storage';
|
||||
import {VCMetadata} from '../../shared/VCMetadata';
|
||||
import {EsignetMosipVCItemMachine} from '../../machines/VCItemMachine/EsignetMosipVCItem/EsignetMosipVCItemMachine';
|
||||
|
||||
const model = createModel(
|
||||
{
|
||||
@@ -24,7 +25,11 @@ const model = createModel(
|
||||
{
|
||||
events: {
|
||||
REFRESH: () => ({}),
|
||||
VIEW_VC: (vcItemActor: ActorRefFrom<typeof vcItemMachine>) => ({
|
||||
VIEW_VC: (
|
||||
vcItemActor:
|
||||
| ActorRefFrom<typeof ExistingMosipVCItemMachine>
|
||||
| ActorRefFrom<typeof EsignetMosipVCItemMachine>,
|
||||
) => ({
|
||||
vcItemActor,
|
||||
}),
|
||||
DISMISS: () => ({}),
|
||||
@@ -35,6 +40,7 @@ const model = createModel(
|
||||
STORAGE_AVAILABLE: () => ({}),
|
||||
STORAGE_UNAVAILABLE: () => ({}),
|
||||
IS_TAMPERED: () => ({}),
|
||||
DOWNLOAD_VIA_ID: () => ({}),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
@@ -6,7 +6,7 @@ import {Centered, Column, Text} from '../../components/ui';
|
||||
import {Theme} from '../../components/ui/styleUtils';
|
||||
import {HomeScreenTabProps} from './HomeScreen';
|
||||
import {useReceivedVcsTab} from './ReceivedVcsTabController';
|
||||
import {VcItem} from '../../components/VcItem';
|
||||
import {ExistingMosipVCItem} from '../../components/VC/ExistingMosipVCItem/ExistingMosipVCItem';
|
||||
|
||||
export const ReceivedVcsTab: React.FC<HomeScreenTabProps> = props => {
|
||||
const {t} = useTranslation('ReceivedVcsTab');
|
||||
@@ -24,7 +24,7 @@ export const ReceivedVcsTab: React.FC<HomeScreenTabProps> = props => {
|
||||
/>
|
||||
}>
|
||||
{controller.receivedVcsMetadata.map(vcMetadata => (
|
||||
<VcItem
|
||||
<ExistingMosipVCItem
|
||||
key={vcMetadata.getVcKey()}
|
||||
vcMetadata={vcMetadata}
|
||||
margin="0 2 8 2"
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { useSelector, useInterpret } from '@xstate/react';
|
||||
import { useContext, useRef, useState } from 'react';
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import {useSelector, useInterpret} from '@xstate/react';
|
||||
import {useContext, useRef, useState} from 'react';
|
||||
import {ActorRefFrom} from 'xstate';
|
||||
import {
|
||||
VcEvents,
|
||||
selectIsRefreshingReceivedVcs,
|
||||
selectReceivedVcsMetadata,
|
||||
} from '../../machines/vc';
|
||||
import { vcItemMachine } from '../../machines/vcItem';
|
||||
import { GlobalContext } from '../../shared/GlobalContext';
|
||||
import {ExistingMosipVCItemMachine} from '../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine';
|
||||
import {GlobalContext} from '../../shared/GlobalContext';
|
||||
import {
|
||||
ReceivedVcsTabEvents,
|
||||
ReceivedVcsTabMachine,
|
||||
} from './ReceivedVcsTabMachine';
|
||||
import { MyVcsTabEvents, MyVcsTabMachine } from './MyVcsTabMachine';
|
||||
import {MyVcsTabEvents, MyVcsTabMachine} from './MyVcsTabMachine';
|
||||
import {
|
||||
HomeScreenEvents,
|
||||
HomeScreenMachine,
|
||||
@@ -24,12 +24,12 @@ import {
|
||||
export function useReceivedVcsTab() {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const machine = useRef(
|
||||
HomeScreenMachine.withContext({
|
||||
...HomeScreenMachine.context,
|
||||
serviceRefs: appService.getSnapshot().context.serviceRefs,
|
||||
})
|
||||
}),
|
||||
);
|
||||
const service = useInterpret(machine.current);
|
||||
|
||||
@@ -54,7 +54,7 @@ export function useReceivedVcsTab() {
|
||||
|
||||
TOGGLE_RECEIVED_CARDS: () => setIsVisible(!isVisible),
|
||||
|
||||
VIEW_VC: (vcRef: ActorRefFrom<typeof vcItemMachine>) => {
|
||||
VIEW_VC: (vcRef: ActorRefFrom<typeof ExistingMosipVCItemMachine>) => {
|
||||
return myVcservice.send(MyVcsTabEvents.VIEW_VC(vcRef));
|
||||
},
|
||||
isViewingVc,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ActorRefFrom, EventFrom, sendParent } from 'xstate';
|
||||
import { createModel } from 'xstate/lib/model';
|
||||
import { vcItemMachine } from '../../machines/vcItem';
|
||||
import { AppServices } from '../../shared/GlobalContext';
|
||||
import {ActorRefFrom, EventFrom, sendParent} from 'xstate';
|
||||
import {createModel} from 'xstate/lib/model';
|
||||
import {ExistingMosipVCItemMachine} from '../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine';
|
||||
import {AppServices} from '../../shared/GlobalContext';
|
||||
|
||||
const model = createModel(
|
||||
{
|
||||
@@ -10,17 +10,19 @@ const model = createModel(
|
||||
},
|
||||
{
|
||||
events: {
|
||||
VIEW_VC: (vcItemActor: ActorRefFrom<typeof vcItemMachine>) => ({
|
||||
VIEW_VC: (
|
||||
vcItemActor: ActorRefFrom<typeof ExistingMosipVCItemMachine>,
|
||||
) => ({
|
||||
vcItemActor,
|
||||
}),
|
||||
REFRESH: () => ({}),
|
||||
DISMISS: () => ({}),
|
||||
STORE_RESPONSE: (response?: unknown) => ({ response }),
|
||||
STORE_ERROR: (error: Error) => ({ error }),
|
||||
ERROR: (error: Error) => ({ error }),
|
||||
GET_RECEIVED_VCS_RESPONSE: (vcMetadatas: string[]) => ({ vcMetadatas }),
|
||||
STORE_RESPONSE: (response?: unknown) => ({response}),
|
||||
STORE_ERROR: (error: Error) => ({error}),
|
||||
ERROR: (error: Error) => ({error}),
|
||||
GET_RECEIVED_VCS_RESPONSE: (vcMetadatas: string[]) => ({vcMetadatas}),
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export const ReceivedVcsTabEvents = model.events;
|
||||
@@ -53,10 +55,10 @@ export const ReceivedVcsTabMachine = model.createMachine(
|
||||
{
|
||||
actions: {
|
||||
viewVcFromParent: sendParent((_context, event) =>
|
||||
model.events.VIEW_VC(event.vcItemActor)
|
||||
model.events.VIEW_VC(event.vcItemActor),
|
||||
),
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export function createReceivedVcsTabMachine(serviceRefs: AppServices) {
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import React from 'react';
|
||||
import { DropdownIcon } from '../../components/DropdownIcon';
|
||||
import { TextEditOverlay } from '../../components/TextEditOverlay';
|
||||
import { Column, Text } from '../../components/ui';
|
||||
import { Modal } from '../../components/ui/Modal';
|
||||
import { MessageOverlay } from '../../components/MessageOverlay';
|
||||
import { ToastItem } from '../../components/ui/ToastItem';
|
||||
import { RevokeConfirmModal } from '../../components/RevokeConfirm';
|
||||
import { OIDcAuthenticationModal } from '../../components/OIDcAuth';
|
||||
import { useViewVcModal, ViewVcModalProps } from './ViewVcModalController';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { VcDetails } from '../../components/VcDetails';
|
||||
import { OtpVerificationModal } from './MyVcs/OtpVerificationModal';
|
||||
import { BindingVcWarningOverlay } from './MyVcs/BindingVcWarningOverlay';
|
||||
import {DropdownIcon} from '../../components/DropdownIcon';
|
||||
import {TextEditOverlay} from '../../components/TextEditOverlay';
|
||||
import {Column, Text} from '../../components/ui';
|
||||
import {Modal} from '../../components/ui/Modal';
|
||||
import {MessageOverlay} from '../../components/MessageOverlay';
|
||||
import {ToastItem} from '../../components/ui/ToastItem';
|
||||
import {RevokeConfirmModal} from '../../components/RevokeConfirm';
|
||||
import {OIDcAuthenticationModal} from '../../components/OIDcAuth';
|
||||
import {useViewVcModal, ViewVcModalProps} from './ViewVcModalController';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {OtpVerificationModal} from './MyVcs/OtpVerificationModal';
|
||||
import {BindingVcWarningOverlay} from './MyVcs/BindingVcWarningOverlay';
|
||||
import {VcDetailsContainer} from '../../components/VC/VcDetailsContainer';
|
||||
|
||||
export const ViewVcModal: React.FC<ViewVcModalProps> = (props) => {
|
||||
const { t } = useTranslation('ViewVcModal');
|
||||
export const ViewVcModal: React.FC<ViewVcModalProps> = props => {
|
||||
const {t} = useTranslation('ViewVcModal');
|
||||
const controller = useViewVcModal(props);
|
||||
|
||||
const DATA = [
|
||||
@@ -39,7 +39,7 @@ export const ViewVcModal: React.FC<ViewVcModalProps> = (props) => {
|
||||
headerElevation={2}>
|
||||
<Column scroll>
|
||||
<Column fill>
|
||||
<VcDetails
|
||||
<VcDetailsContainer
|
||||
vc={controller.vc}
|
||||
onBinding={controller.addtoWallet}
|
||||
isBindingPending={controller.isWalletBindingPending}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useMachine, useSelector } from '@xstate/react';
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
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 {ModalProps} from '../../components/ui/Modal';
|
||||
import {GlobalContext} from '../../shared/GlobalContext';
|
||||
import {
|
||||
selectOtpError,
|
||||
selectIsAcceptingOtpInput,
|
||||
@@ -14,8 +14,8 @@ import {
|
||||
selectIsRevokingVc,
|
||||
selectIsLoggingRevoke,
|
||||
selectVc,
|
||||
VcItemEvents,
|
||||
vcItemMachine,
|
||||
ExistingMosipVCItemEvents,
|
||||
ExistingMosipVCItemMachine,
|
||||
selectWalletBindingError,
|
||||
selectRequestBindingOtp,
|
||||
selectAcceptingBindingOtp,
|
||||
@@ -23,22 +23,22 @@ import {
|
||||
selectWalletBindingInProgress,
|
||||
selectShowWalletBindingError,
|
||||
selectBindingWarning,
|
||||
} from '../../machines/vcItem';
|
||||
import { selectPasscode } from '../../machines/auth';
|
||||
import { biometricsMachine, selectIsSuccess } from '../../machines/biometrics';
|
||||
} from '../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine';
|
||||
import {selectPasscode} from '../../machines/auth';
|
||||
import {biometricsMachine, selectIsSuccess} from '../../machines/biometrics';
|
||||
|
||||
export function useViewVcModal({
|
||||
vcItemActor,
|
||||
isVisible,
|
||||
onRevokeDelete,
|
||||
}: ViewVcModalProps) {
|
||||
const { t } = useTranslation('ViewVcModal');
|
||||
const {t} = useTranslation('ViewVcModal');
|
||||
const [toastVisible, setToastVisible] = useState(false);
|
||||
const [message, setMessage] = useState('');
|
||||
const [reAuthenticating, setReAuthenticating] = useState('');
|
||||
const [isRevoking, setRevoking] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const authService = appService.children.get('auth');
|
||||
const [, bioSend, bioService] = useMachine(biometricsMachine);
|
||||
|
||||
@@ -49,10 +49,10 @@ export function useViewVcModal({
|
||||
const vc = useSelector(vcItemActor, selectVc);
|
||||
const otError = useSelector(vcItemActor, selectOtpError);
|
||||
const onSuccess = () => {
|
||||
bioSend({ type: 'SET_IS_AVAILABLE', data: true });
|
||||
bioSend({type: 'SET_IS_AVAILABLE', data: true});
|
||||
setError('');
|
||||
setReAuthenticating('');
|
||||
vcItemActor.send(VcItemEvents.LOCK_VC());
|
||||
vcItemActor.send(ExistingMosipVCItemEvents.LOCK_VC());
|
||||
};
|
||||
|
||||
const onError = (value: string) => {
|
||||
@@ -69,11 +69,11 @@ export function useViewVcModal({
|
||||
};
|
||||
|
||||
const netInfoFetch = (otp: string) => {
|
||||
NetInfo.fetch().then((state) => {
|
||||
NetInfo.fetch().then(state => {
|
||||
if (state.isConnected) {
|
||||
vcItemActor.send(VcItemEvents.INPUT_OTP(otp));
|
||||
vcItemActor.send(ExistingMosipVCItemEvents.INPUT_OTP(otp));
|
||||
} else {
|
||||
vcItemActor.send(VcItemEvents.DISMISS());
|
||||
vcItemActor.send(ExistingMosipVCItemEvents.DISMISS());
|
||||
showToast('Request network failed');
|
||||
}
|
||||
});
|
||||
@@ -84,10 +84,10 @@ export function useViewVcModal({
|
||||
showToast(vc.locked ? t('success.locked') : t('success.unlocked'));
|
||||
}
|
||||
if (isRevokingVc) {
|
||||
showToast(t('success.revoked', { vid: vc.id }));
|
||||
showToast(t('success.revoked', {vid: vc.id}));
|
||||
}
|
||||
if (isLoggingRevoke) {
|
||||
vcItemActor.send(VcItemEvents.DISMISS());
|
||||
vcItemActor.send(ExistingMosipVCItemEvents.DISMISS());
|
||||
onRevokeDelete();
|
||||
}
|
||||
if (isSuccessBio && reAuthenticating != '') {
|
||||
@@ -104,7 +104,7 @@ export function useViewVcModal({
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
vcItemActor.send(VcItemEvents.REFRESH());
|
||||
vcItemActor.send(ExistingMosipVCItemEvents.REFRESH());
|
||||
}, [isVisible]);
|
||||
return {
|
||||
error,
|
||||
@@ -120,7 +120,7 @@ export function useViewVcModal({
|
||||
isAcceptingOtpInput: useSelector(vcItemActor, selectIsAcceptingOtpInput),
|
||||
isAcceptingRevokeInput: useSelector(
|
||||
vcItemActor,
|
||||
selectIsAcceptingRevokeInput
|
||||
selectIsAcceptingRevokeInput,
|
||||
),
|
||||
storedPasscode: useSelector(authService, selectPasscode),
|
||||
isBindingOtp: useSelector(vcItemActor, selectRequestBindingOtp),
|
||||
@@ -128,11 +128,11 @@ export function useViewVcModal({
|
||||
walletBindingError: useSelector(vcItemActor, selectWalletBindingError),
|
||||
isWalletBindingPending: useSelector(
|
||||
vcItemActor,
|
||||
selectEmptyWalletBindingId
|
||||
selectEmptyWalletBindingId,
|
||||
),
|
||||
isWalletBindingInProgress: useSelector(
|
||||
vcItemActor,
|
||||
selectWalletBindingInProgress
|
||||
selectWalletBindingInProgress,
|
||||
),
|
||||
isBindingError: useSelector(vcItemActor, selectShowWalletBindingError),
|
||||
isBindingWarning: useSelector(vcItemActor, selectBindingWarning),
|
||||
@@ -141,17 +141,17 @@ export function useViewVcModal({
|
||||
setRevoking(true);
|
||||
},
|
||||
REVOKE_VC: () => {
|
||||
vcItemActor.send(VcItemEvents.REVOKE_VC());
|
||||
vcItemActor.send(ExistingMosipVCItemEvents.REVOKE_VC());
|
||||
setRevoking(false);
|
||||
},
|
||||
setReAuthenticating,
|
||||
setRevoking,
|
||||
onError,
|
||||
addtoWallet: () => {
|
||||
vcItemActor.send(VcItemEvents.ADD_WALLET_BINDING_ID());
|
||||
vcItemActor.send(ExistingMosipVCItemEvents.ADD_WALLET_BINDING_ID());
|
||||
},
|
||||
lockVc: () => {
|
||||
vcItemActor.send(VcItemEvents.LOCK_VC());
|
||||
vcItemActor.send(ExistingMosipVCItemEvents.LOCK_VC());
|
||||
},
|
||||
inputOtp: (otp: string) => {
|
||||
netInfoFetch(otp);
|
||||
@@ -159,20 +159,23 @@ export function useViewVcModal({
|
||||
revokeVc: (otp: string) => {
|
||||
netInfoFetch(otp);
|
||||
},
|
||||
ADD_WALLET: () => vcItemActor.send(VcItemEvents.ADD_WALLET_BINDING_ID()),
|
||||
ADD_WALLET: () =>
|
||||
vcItemActor.send(ExistingMosipVCItemEvents.ADD_WALLET_BINDING_ID()),
|
||||
onSuccess,
|
||||
EDIT_TAG: () => vcItemActor.send(VcItemEvents.EDIT_TAG()),
|
||||
SAVE_TAG: (tag: string) => vcItemActor.send(VcItemEvents.SAVE_TAG(tag)),
|
||||
DISMISS: () => vcItemActor.send(VcItemEvents.DISMISS()),
|
||||
LOCK_VC: () => vcItemActor.send(VcItemEvents.LOCK_VC()),
|
||||
INPUT_OTP: (otp: string) => vcItemActor.send(VcItemEvents.INPUT_OTP(otp)),
|
||||
CANCEL: () => vcItemActor.send(VcItemEvents.CANCEL()),
|
||||
CONFIRM: () => vcItemActor.send(VcItemEvents.CONFIRM()),
|
||||
EDIT_TAG: () => vcItemActor.send(ExistingMosipVCItemEvents.EDIT_TAG()),
|
||||
SAVE_TAG: (tag: string) =>
|
||||
vcItemActor.send(ExistingMosipVCItemEvents.SAVE_TAG(tag)),
|
||||
DISMISS: () => vcItemActor.send(ExistingMosipVCItemEvents.DISMISS()),
|
||||
LOCK_VC: () => vcItemActor.send(ExistingMosipVCItemEvents.LOCK_VC()),
|
||||
INPUT_OTP: (otp: string) =>
|
||||
vcItemActor.send(ExistingMosipVCItemEvents.INPUT_OTP(otp)),
|
||||
CANCEL: () => vcItemActor.send(ExistingMosipVCItemEvents.CANCEL()),
|
||||
CONFIRM: () => vcItemActor.send(ExistingMosipVCItemEvents.CONFIRM()),
|
||||
};
|
||||
}
|
||||
|
||||
export interface ViewVcModalProps extends ModalProps {
|
||||
vcItemActor: ActorRefFrom<typeof vcItemMachine>;
|
||||
vcItemActor: ActorRefFrom<typeof ExistingMosipVCItemMachine>;
|
||||
onDismiss: () => void;
|
||||
onRevokeDelete: () => void;
|
||||
activeTab: Number;
|
||||
|
||||
102
screens/HomeScreenLayout.tsx
Normal file
102
screens/HomeScreenLayout.tsx
Normal file
@@ -0,0 +1,102 @@
|
||||
import {getFocusedRouteNameFromRoute} from '@react-navigation/native';
|
||||
import {createNativeStackNavigator} from '@react-navigation/native-stack';
|
||||
import React from 'react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {Image} from 'react-native';
|
||||
import {Icon} from 'react-native-elements';
|
||||
import {HelpScreen} from '../components/HelpScreen';
|
||||
import {Row} from '../components/ui';
|
||||
import {Header} from '../components/ui/Header';
|
||||
import {Theme} from '../components/ui/styleUtils';
|
||||
import {RootRouteProps} from '../routes';
|
||||
import {HomeScreen} from './Home/HomeScreen';
|
||||
import {IssuersScreen} from './Issuers/IssuersScreen';
|
||||
import {SettingScreen} from './Settings/SettingScreen';
|
||||
|
||||
const {Navigator, Screen} = createNativeStackNavigator();
|
||||
export const HomeScreenLayout: React.FC<RootRouteProps> = props => {
|
||||
const {t} = useTranslation('IssuersScreen');
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
const routeName = getFocusedRouteNameFromRoute(props.route);
|
||||
if (routeName === 'IssuersScreen') {
|
||||
props.navigation.setOptions({tabBarStyle: {display: 'none'}});
|
||||
} else {
|
||||
props.navigation.setOptions({
|
||||
tabBarShowLabel: true,
|
||||
tabBarActiveTintColor: Theme.Colors.IconBg,
|
||||
tabBarLabelStyle: {
|
||||
fontSize: 12,
|
||||
fontFamily: 'Inter_600SemiBold',
|
||||
},
|
||||
tabBarStyle: {
|
||||
height: 75,
|
||||
paddingHorizontal: 10,
|
||||
},
|
||||
tabBarItemStyle: {
|
||||
height: 83,
|
||||
padding: 11,
|
||||
},
|
||||
});
|
||||
}
|
||||
}, [props.navigation, props.route]);
|
||||
|
||||
const HomeScreenOptions = {
|
||||
headerLeft: () =>
|
||||
React.createElement(Image, {
|
||||
source: Theme.InjiHomeLogo,
|
||||
style: {width: 124, height: 27, resizeMode: 'contain'},
|
||||
}),
|
||||
headerTitle: '',
|
||||
headerRight: () => (
|
||||
<Row align="space-between">
|
||||
<HelpScreen
|
||||
triggerComponent={
|
||||
<Image source={Theme.HelpIcon} style={{width: 36, height: 36}} />
|
||||
}
|
||||
navigation={undefined}
|
||||
route={undefined}
|
||||
/>
|
||||
|
||||
<SettingScreen
|
||||
triggerComponent={
|
||||
<Icon
|
||||
name="settings"
|
||||
type="simple-line-icon"
|
||||
size={21}
|
||||
style={Theme.Styles.IconContainer}
|
||||
color={Theme.Colors.Icon}
|
||||
/>
|
||||
}
|
||||
navigation={props.navigation}
|
||||
route={undefined}
|
||||
/>
|
||||
</Row>
|
||||
),
|
||||
};
|
||||
|
||||
return (
|
||||
<Navigator>
|
||||
<Screen
|
||||
key={'HomeScreen'}
|
||||
name={'HomeScreen'}
|
||||
component={HomeScreen}
|
||||
options={HomeScreenOptions}
|
||||
/>
|
||||
<Screen
|
||||
key={'Issuers'}
|
||||
name={'IssuersScreen'}
|
||||
component={IssuersScreen}
|
||||
options={{
|
||||
header: props => (
|
||||
<Header
|
||||
goBack={props.navigation.goBack}
|
||||
title={t('title')}
|
||||
testID="issuersScreenHeader"
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Navigator>
|
||||
);
|
||||
};
|
||||
45
screens/Issuers/IssuerScreenController.tsx
Normal file
45
screens/Issuers/IssuerScreenController.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import {useSelector} from '@xstate/react';
|
||||
import {
|
||||
IssuerScreenTabEvents,
|
||||
IssuersMachine,
|
||||
selectErrorMessage,
|
||||
selectIsDone,
|
||||
selectIsDownloadCredentials,
|
||||
selectIsIdle,
|
||||
selectIssuers,
|
||||
selectLoadingReason,
|
||||
selectStoring,
|
||||
} from '../../machines/issuersMachine';
|
||||
import {ActorRefFrom} from 'xstate';
|
||||
import {BOTTOM_TAB_ROUTES} from '../../routes/routesConstants';
|
||||
|
||||
export function useIssuerScreenController({route, navigation}) {
|
||||
const service = route.params.service;
|
||||
|
||||
return {
|
||||
issuers: useSelector(service, selectIssuers),
|
||||
errorMessage: useSelector(service, selectErrorMessage),
|
||||
isDownloadingCredentials: useSelector(service, selectIsDownloadCredentials),
|
||||
isDone: useSelector(service, selectIsDone),
|
||||
isIdle: useSelector(service, selectIsIdle),
|
||||
loadingReason: useSelector(service, selectLoadingReason),
|
||||
isStoring: useSelector(service, selectStoring),
|
||||
|
||||
CANCEL: () => service.send(IssuerScreenTabEvents.CANCEL()),
|
||||
SELECTED_ISSUER: id =>
|
||||
service.send(IssuerScreenTabEvents.SELECTED_ISSUER(id)),
|
||||
DISMISS: () => service.send(IssuerScreenTabEvents.DISMISS()),
|
||||
TRY_AGAIN: () => service.send(IssuerScreenTabEvents.TRY_AGAIN()),
|
||||
RESET_ERROR: () => service.send(IssuerScreenTabEvents.RESET_ERROR()),
|
||||
DOWNLOAD_ID: () => {
|
||||
service.send(IssuerScreenTabEvents.DOWNLOAD_ID());
|
||||
navigation.navigate(BOTTOM_TAB_ROUTES.home, {screen: 'HomeScreen'});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export interface IssuerModalProps {
|
||||
service?: ActorRefFrom<typeof IssuersMachine>;
|
||||
onPress?: () => void;
|
||||
isVisible?: boolean;
|
||||
}
|
||||
141
screens/Issuers/IssuersScreen.tsx
Normal file
141
screens/Issuers/IssuersScreen.tsx
Normal file
@@ -0,0 +1,141 @@
|
||||
import React, {useLayoutEffect} from 'react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {FlatList, Image, Text, View} from 'react-native';
|
||||
import {Issuer} from '../../components/openId4VCI/Issuer';
|
||||
import {Error} from '../../components/ui/Error';
|
||||
import {Header} from '../../components/ui/Header';
|
||||
import {Column} from '../../components/ui/Layout';
|
||||
import {Theme} from '../../components/ui/styleUtils';
|
||||
import {RootRouteProps} from '../../routes';
|
||||
import {HomeRouteProps} from '../../routes/main';
|
||||
import {useIssuerScreenController} from './IssuerScreenController';
|
||||
import {Loader} from '../../components/ui/Loader';
|
||||
import testIDProps from '../../shared/commonUtil';
|
||||
|
||||
export const IssuersScreen: React.FC<
|
||||
HomeRouteProps | RootRouteProps
|
||||
> = props => {
|
||||
const controller = useIssuerScreenController(props);
|
||||
const {t} = useTranslation('IssuersScreen');
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (controller.loadingReason || controller.errorMessage) {
|
||||
props.navigation.setOptions({
|
||||
headerShown: false,
|
||||
});
|
||||
} else {
|
||||
props.navigation.setOptions({
|
||||
headerShown: true,
|
||||
header: props => (
|
||||
<Header
|
||||
goBack={props.navigation.goBack}
|
||||
title={t('title')}
|
||||
testID="issuersScreenHeader"
|
||||
/>
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
if (controller.isStoring) {
|
||||
props.navigation.goBack();
|
||||
}
|
||||
}, [controller.loadingReason, controller.errorMessage, controller.isStoring]);
|
||||
|
||||
const onPressHandler = (id: string) => {
|
||||
if (id !== 'UIN, VID, AID') {
|
||||
controller.SELECTED_ISSUER(id);
|
||||
} else {
|
||||
controller.DOWNLOAD_ID();
|
||||
}
|
||||
};
|
||||
|
||||
const isGenericError = () => {
|
||||
return controller.errorMessage === 'generic';
|
||||
};
|
||||
|
||||
const goBack = () => {
|
||||
controller.RESET_ERROR();
|
||||
setTimeout(() => {
|
||||
props.navigation.goBack();
|
||||
}, 0);
|
||||
};
|
||||
|
||||
const getImage = () => {
|
||||
if (isGenericError()) {
|
||||
return (
|
||||
<Image
|
||||
source={Theme.SomethingWentWrong}
|
||||
style={{width: 370, height: 150}}
|
||||
{...testIDProps('somethingWentWrongImage')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Image
|
||||
{...testIDProps('noInternetConnectionImage')}
|
||||
source={Theme.NoInternetConnection}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
if (controller.loadingReason) {
|
||||
return (
|
||||
<Loader
|
||||
isVisible
|
||||
title={t('loaders.loading')}
|
||||
subTitle={t(`loaders.subTitle.${controller.loadingReason}`)}
|
||||
progress
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{controller.issuers.length > 0 && (
|
||||
<Column style={Theme.issuersScreenStyles.issuerListOuterContainer}>
|
||||
<Text
|
||||
{...testIDProps('addCardDescription')}
|
||||
style={{
|
||||
...Theme.TextStyles.regularGrey,
|
||||
marginVertical: 14,
|
||||
marginHorizontal: 9,
|
||||
}}>
|
||||
{t('header')}
|
||||
</Text>
|
||||
<View style={Theme.issuersScreenStyles.issuersContainer}>
|
||||
{controller.issuers.length > 0 && (
|
||||
<FlatList
|
||||
data={controller.issuers}
|
||||
scrollEnabled={false}
|
||||
renderItem={({item}) => (
|
||||
<Issuer
|
||||
testID={`issuer-${item.id}`}
|
||||
key={item.id}
|
||||
id={item.id}
|
||||
displayName={item.displayName}
|
||||
logoUrl={item.logoUrl}
|
||||
onPress={() => onPressHandler(item.id)}
|
||||
{...props}
|
||||
/>
|
||||
)}
|
||||
numColumns={2}
|
||||
keyExtractor={item => item.id}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</Column>
|
||||
)}
|
||||
{controller.errorMessage && (
|
||||
<Error
|
||||
testID={`${controller.errorMessage}Error`}
|
||||
isVisible={controller.errorMessage !== ''}
|
||||
title={t(`errors.${controller.errorMessage}.title`)}
|
||||
message={t(`errors.${controller.errorMessage}.message`)}
|
||||
goBack={goBack}
|
||||
tryAgain={isGenericError() ? null : controller.TRY_AGAIN}
|
||||
image={getImage()}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
@@ -1,16 +1,16 @@
|
||||
import React from 'react';
|
||||
import { Button, Column, Text, Centered } from '../../components/ui';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { VcItem } from '../../components/VcItem';
|
||||
import { useQrLogin } from './QrLoginController';
|
||||
import { QrLoginRef } from '../../machines/QrLoginMachine';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { Modal } from '../../components/ui/Modal';
|
||||
import {Button, Column, Text, Centered} from '../../components/ui';
|
||||
import {Theme} from '../../components/ui/styleUtils';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {ExistingMosipVCItem} from '../../components/VC/ExistingMosipVCItem/ExistingMosipVCItem';
|
||||
import {useQrLogin} from './QrLoginController';
|
||||
import {QrLoginRef} from '../../machines/QrLoginMachine';
|
||||
import {Icon} from 'react-native-elements';
|
||||
import {Modal} from '../../components/ui/Modal';
|
||||
|
||||
export const MyBindedVcs: React.FC<MyBindedVcsProps> = (props) => {
|
||||
export const MyBindedVcs: React.FC<MyBindedVcsProps> = props => {
|
||||
const controller = useQrLogin(props);
|
||||
const { t } = useTranslation('QrScreen');
|
||||
const {t} = useTranslation('QrScreen');
|
||||
|
||||
return (
|
||||
<Modal
|
||||
@@ -22,7 +22,7 @@ export const MyBindedVcs: React.FC<MyBindedVcsProps> = (props) => {
|
||||
controller.DISMISS();
|
||||
}}>
|
||||
<React.Fragment>
|
||||
<Column fill style={{ display: props.isVisible ? 'flex' : 'none' }}>
|
||||
<Column fill style={{display: props.isVisible ? 'flex' : 'none'}}>
|
||||
<Column fill>
|
||||
{controller.shareableVcsMetadata.length > 0 && (
|
||||
<>
|
||||
@@ -34,7 +34,7 @@ export const MyBindedVcs: React.FC<MyBindedVcsProps> = (props) => {
|
||||
{controller.shareableVcsMetadata.length > 0 &&
|
||||
controller.shareableVcsMetadata.map(
|
||||
(vcMetadata, index) => (
|
||||
<VcItem
|
||||
<ExistingMosipVCItem
|
||||
key={vcMetadata.getVcKey()}
|
||||
vcMetadata={vcMetadata}
|
||||
margin="0 2 8 2"
|
||||
@@ -43,7 +43,7 @@ export const MyBindedVcs: React.FC<MyBindedVcsProps> = (props) => {
|
||||
selectable
|
||||
selected={index === controller.selectedIndex}
|
||||
/>
|
||||
)
|
||||
),
|
||||
)}
|
||||
</Column>
|
||||
</Column>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useSelector } from '@xstate/react';
|
||||
import { useContext, useState } from 'react';
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import {useSelector} from '@xstate/react';
|
||||
import {useContext, useState} from 'react';
|
||||
import {ActorRefFrom} from 'xstate';
|
||||
import {
|
||||
QrLoginEvents,
|
||||
selectClientName,
|
||||
@@ -24,14 +24,14 @@ import {
|
||||
selectIsSendingAuthenticate,
|
||||
selectEssentialClaims,
|
||||
} from '../../machines/QrLoginMachine';
|
||||
import { selectBindedVcsMetadata } from '../../machines/vc';
|
||||
import { vcItemMachine } from '../../machines/vcItem';
|
||||
import { GlobalContext } from '../../shared/GlobalContext';
|
||||
import { VC } from '../../types/vc';
|
||||
import { QrLoginProps } from './QrLogin';
|
||||
import {selectBindedVcsMetadata} from '../../machines/vc';
|
||||
import {ExistingMosipVCItemMachine} from '../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine';
|
||||
import {GlobalContext} from '../../shared/GlobalContext';
|
||||
import {VC} from '../../types/vc';
|
||||
import {QrLoginProps} from './QrLogin';
|
||||
|
||||
export function useQrLogin({ service }: QrLoginProps) {
|
||||
const { appService } = useContext(GlobalContext);
|
||||
export function useQrLogin({service}: QrLoginProps) {
|
||||
const {appService} = useContext(GlobalContext);
|
||||
|
||||
const vcService = appService.children.get('vc');
|
||||
const [selectedIndex, setSelectedIndex] = useState<number>(null);
|
||||
@@ -45,7 +45,8 @@ export function useQrLogin({ service }: QrLoginProps) {
|
||||
|
||||
return {
|
||||
SELECT_VC_ITEM:
|
||||
(index: number) => (vcRef: ActorRefFrom<typeof vcItemMachine>) => {
|
||||
(index: number) =>
|
||||
(vcRef: ActorRefFrom<typeof ExistingMosipVCItemMachine>) => {
|
||||
setSelectedIndex(index);
|
||||
const vcData = vcRef.getSnapshot().context;
|
||||
SELECT_VC(vcData);
|
||||
@@ -55,7 +56,7 @@ export function useQrLogin({ service }: QrLoginProps) {
|
||||
selectedVc: useSelector(service, selectSelectedVc),
|
||||
linkTransactionResponse: useSelector(
|
||||
service,
|
||||
selectLinkTransactionResponse
|
||||
selectLinkTransactionResponse,
|
||||
),
|
||||
domainName: useSelector(service, selectDomainName),
|
||||
logoUrl: useSelector(service, selectLogoUrl),
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
|
||||
import { DeviceInfoList } from '../../components/DeviceInfoList';
|
||||
import { Button, Column, Row, Text } from '../../components/ui';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { VcDetails } from '../../components/VcDetails';
|
||||
import { useReceiveVcScreen } from './ReceiveVcScreenController';
|
||||
import { VerifyIdentityOverlay } from '../VerifyIdentityOverlay';
|
||||
import {DeviceInfoList} from '../../components/DeviceInfoList';
|
||||
import {Button, Column, Row, Text} from '../../components/ui';
|
||||
import {Theme} from '../../components/ui/styleUtils';
|
||||
import {ExistingMosipVCItemDetails} from '../../components/VC/ExistingMosipVCItem/ExistingMosipVCItemDetails';
|
||||
import {useReceiveVcScreen} from './ReceiveVcScreenController';
|
||||
import {VerifyIdentityOverlay} from '../VerifyIdentityOverlay';
|
||||
import {
|
||||
MessageOverlay,
|
||||
ErrorMessageOverlay,
|
||||
} from '../../components/MessageOverlay';
|
||||
import { useOverlayVisibleAfterTimeout } from '../../shared/hooks/useOverlayVisibleAfterTimeout';
|
||||
import {useOverlayVisibleAfterTimeout} from '../../shared/hooks/useOverlayVisibleAfterTimeout';
|
||||
|
||||
export const ReceiveVcScreen: React.FC = () => {
|
||||
const { t } = useTranslation('ReceiveVcScreen');
|
||||
const {t} = useTranslation('ReceiveVcScreen');
|
||||
const controller = useReceiveVcScreen();
|
||||
const savingOverlayVisible = useOverlayVisibleAfterTimeout(
|
||||
controller.isAccepting
|
||||
controller.isAccepting,
|
||||
);
|
||||
const storeErrorTranslationPath = 'errors.savingFailed';
|
||||
|
||||
@@ -32,7 +32,7 @@ export const ReceiveVcScreen: React.FC = () => {
|
||||
<Text weight="semibold" margin="24 24 0 24">
|
||||
{t('header')}
|
||||
</Text>
|
||||
<VcDetails
|
||||
<ExistingMosipVCItemDetails
|
||||
vc={controller.incomingVc}
|
||||
isBindingPending={false}
|
||||
activeTab={1}
|
||||
@@ -59,7 +59,7 @@ export const ReceiveVcScreen: React.FC = () => {
|
||||
isVisible={controller.isInvalidIdentity}
|
||||
title={t('VerifyIdentityOverlay:errors.invalidIdentity.title')}
|
||||
message={t(
|
||||
'VerifyIdentityOverlay:errors.invalidIdentity.messageNoRetry'
|
||||
'VerifyIdentityOverlay:errors.invalidIdentity.messageNoRetry',
|
||||
)}
|
||||
onBackdropPress={controller.DISMISS}>
|
||||
<Row>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Dimensions } from 'react-native';
|
||||
import { Overlay } from 'react-native-elements/dist/overlay/Overlay';
|
||||
import { Button, Column, Text } from '../../components/ui';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { VcItem } from '../../components/VcItem';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {Dimensions} from 'react-native';
|
||||
import {Overlay} from 'react-native-elements/dist/overlay/Overlay';
|
||||
import {Button, Column, Text} from '../../components/ui';
|
||||
import {Theme} from '../../components/ui/styleUtils';
|
||||
import {ExistingMosipVCItem} from '../../components/VC/ExistingMosipVCItem/ExistingMosipVCItem';
|
||||
import {
|
||||
SelectVcOverlayProps,
|
||||
useSelectVcOverlay,
|
||||
} from './SelectVcOverlayController';
|
||||
|
||||
export const SelectVcOverlay: React.FC<SelectVcOverlayProps> = (props) => {
|
||||
const { t } = useTranslation('SelectVcOverlay');
|
||||
export const SelectVcOverlay: React.FC<SelectVcOverlayProps> = props => {
|
||||
const {t} = useTranslation('SelectVcOverlay');
|
||||
const controller = useSelectVcOverlay(props);
|
||||
|
||||
return (
|
||||
@@ -21,7 +21,7 @@ export const SelectVcOverlay: React.FC<SelectVcOverlayProps> = (props) => {
|
||||
<Column
|
||||
padding="24"
|
||||
width={Dimensions.get('screen').width * 0.9}
|
||||
style={{ maxHeight: Dimensions.get('screen').height * 0.9 }}>
|
||||
style={{maxHeight: Dimensions.get('screen').height * 0.9}}>
|
||||
<Text weight="semibold" margin="0 0 16 0">
|
||||
{t('header')}
|
||||
</Text>
|
||||
@@ -30,7 +30,7 @@ export const SelectVcOverlay: React.FC<SelectVcOverlayProps> = (props) => {
|
||||
</Text>
|
||||
<Column margin="0 0 32 0" scroll>
|
||||
{props.vcMetadatas.map((vcMetadata, index) => (
|
||||
<VcItem
|
||||
<ExistingMosipVCItem
|
||||
key={`${vcMetadata.getVcKey()}-${index}`}
|
||||
vcMetadata={vcMetadata}
|
||||
margin="0 2 8 2"
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
import { useState } from 'react';
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import { vcItemMachine } from '../../machines/vcItem';
|
||||
import { VC } from '../../types/vc';
|
||||
import { VCMetadata } from '../../shared/VCMetadata';
|
||||
import {useState} from 'react';
|
||||
import {ActorRefFrom} from 'xstate';
|
||||
import {ExistingMosipVCItemMachine} from '../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine';
|
||||
import {VC} from '../../types/vc';
|
||||
import {VCMetadata} from '../../shared/VCMetadata';
|
||||
|
||||
export function useSelectVcOverlay(props: SelectVcOverlayProps) {
|
||||
const [selectedIndex, setSelectedIndex] = useState<number>(null);
|
||||
const [selectedVcRef, setSelectedVcRef] =
|
||||
useState<ActorRefFrom<typeof vcItemMachine>>(null);
|
||||
useState<ActorRefFrom<typeof ExistingMosipVCItemMachine>>(null);
|
||||
|
||||
return {
|
||||
selectVcItem,
|
||||
selectedIndex,
|
||||
|
||||
onSelect: () => {
|
||||
const { serviceRefs, ...vc } = selectedVcRef.getSnapshot().context;
|
||||
const {serviceRefs, ...vc} = selectedVcRef.getSnapshot().context;
|
||||
props.onSelect(vc);
|
||||
},
|
||||
|
||||
onVerifyAndSelect: () => {
|
||||
const { serviceRefs, ...vc } = selectedVcRef.getSnapshot().context;
|
||||
const {serviceRefs, ...vc} = selectedVcRef.getSnapshot().context;
|
||||
props.onVerifyAndSelect(vc);
|
||||
},
|
||||
};
|
||||
|
||||
function selectVcItem(index: number) {
|
||||
return (vcRef: ActorRefFrom<typeof vcItemMachine>) => {
|
||||
return (vcRef: ActorRefFrom<typeof ExistingMosipVCItemMachine>) => {
|
||||
setSelectedIndex(index);
|
||||
setSelectedVcRef(vcRef);
|
||||
};
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
import React, { useContext, useEffect, useRef } from 'react';
|
||||
import { Input } from 'react-native-elements';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button, Column, Row, Text } from '../../components/ui';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { MessageOverlay } from '../../components/MessageOverlay';
|
||||
import { useSendVcScreen } from './SendVcScreenController';
|
||||
import { VerifyIdentityOverlay } from '../VerifyIdentityOverlay';
|
||||
import { VcItem } from '../../components/VcItem';
|
||||
import { I18nManager, BackHandler } from 'react-native';
|
||||
import { useInterpret } from '@xstate/react';
|
||||
import { createVcItemMachine } from '../../machines/vcItem';
|
||||
import { GlobalContext } from '../../shared/GlobalContext';
|
||||
import { useFocusEffect } from '@react-navigation/native';
|
||||
import React, {useContext, useEffect, useRef} from 'react';
|
||||
import {Input} from 'react-native-elements';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {Button, Column, Row, Text} from '../../components/ui';
|
||||
import {Theme} from '../../components/ui/styleUtils';
|
||||
import {MessageOverlay} from '../../components/MessageOverlay';
|
||||
import {useSendVcScreen} from './SendVcScreenController';
|
||||
import {VerifyIdentityOverlay} from '../VerifyIdentityOverlay';
|
||||
import {ExistingMosipVCItem} from '../../components/VC/ExistingMosipVCItem/ExistingMosipVCItem';
|
||||
import {I18nManager, 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';
|
||||
|
||||
export const SendVcScreen: React.FC = () => {
|
||||
const { t } = useTranslation('SendVcScreen');
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const {t} = useTranslation('SendVcScreen');
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const controller = useSendVcScreen();
|
||||
let service;
|
||||
|
||||
if (controller.shareableVcsMetadata?.length > 0) {
|
||||
const firstVCMachine = useRef(
|
||||
createVcItemMachine(
|
||||
createExistingMosipVCItemMachine(
|
||||
appService.getSnapshot().context.serviceRefs,
|
||||
controller.shareableVcsMetadata[0]
|
||||
)
|
||||
controller.shareableVcsMetadata[0],
|
||||
),
|
||||
);
|
||||
|
||||
service = useInterpret(firstVCMachine.current);
|
||||
@@ -42,11 +42,11 @@ export const SendVcScreen: React.FC = () => {
|
||||
|
||||
const disableBackHandler = BackHandler.addEventListener(
|
||||
'hardwareBackPress',
|
||||
onBackPress
|
||||
onBackPress,
|
||||
);
|
||||
|
||||
return () => disableBackHandler.remove();
|
||||
}, [])
|
||||
}, []),
|
||||
);
|
||||
|
||||
const reasonLabel = t('reasonForSharing');
|
||||
@@ -58,28 +58,28 @@ export const SendVcScreen: React.FC = () => {
|
||||
<Column
|
||||
padding="24 19 14 19"
|
||||
backgroundColor={Theme.Colors.whiteBackgroundColor}
|
||||
style={{ position: 'relative' }}>
|
||||
style={{position: 'relative'}}>
|
||||
<Input
|
||||
value={controller.reason ? controller.reason : ''}
|
||||
placeholder={!controller.reason ? reasonLabel : ''}
|
||||
label={controller.reason ? reasonLabel : ''}
|
||||
labelStyle={{ textAlign: 'left' }}
|
||||
labelStyle={{textAlign: 'left'}}
|
||||
onChangeText={controller.UPDATE_REASON}
|
||||
containerStyle={{ marginBottom: 6 }}
|
||||
inputStyle={{ textAlign: I18nManager.isRTL ? 'right' : 'left' }}
|
||||
containerStyle={{marginBottom: 6}}
|
||||
inputStyle={{textAlign: I18nManager.isRTL ? 'right' : 'left'}}
|
||||
/>
|
||||
</Column>
|
||||
<Text
|
||||
margin="15 0 13 24"
|
||||
weight="bold"
|
||||
color={Theme.Colors.textValue}
|
||||
style={{ position: 'relative' }}>
|
||||
style={{position: 'relative'}}>
|
||||
{t('pleaseSelectAnId')}
|
||||
</Text>
|
||||
</Column>
|
||||
<Column scroll>
|
||||
{controller.shareableVcsMetadata.map((vcMetadata, index) => (
|
||||
<VcItem
|
||||
<ExistingMosipVCItem
|
||||
key={vcMetadata.getVcKey()}
|
||||
vcMetadata={vcMetadata}
|
||||
margin="0 2 8 2"
|
||||
@@ -99,7 +99,7 @@ export const SendVcScreen: React.FC = () => {
|
||||
<Button
|
||||
type="gradient"
|
||||
title={t('acceptRequestAndVerify')}
|
||||
styles={{ marginTop: 12 }}
|
||||
styles={{marginTop: 12}}
|
||||
disabled={controller.selectedIndex == null}
|
||||
onPress={controller.VERIFY_AND_ACCEPT_REQUEST}
|
||||
/>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useSelector } from '@xstate/react';
|
||||
import { useContext, useState } from 'react';
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import { selectShareableVcsMetadata } from '../../machines/vc';
|
||||
import { vcItemMachine } from '../../machines/vcItem';
|
||||
import { GlobalContext } from '../../shared/GlobalContext';
|
||||
import {useSelector} from '@xstate/react';
|
||||
import {useContext, useState} from 'react';
|
||||
import {ActorRefFrom} from 'xstate';
|
||||
import {selectShareableVcsMetadata} from '../../machines/vc';
|
||||
import {ExistingMosipVCItemMachine} from '../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine';
|
||||
import {GlobalContext} from '../../shared/GlobalContext';
|
||||
import {
|
||||
selectIsSelectingVc,
|
||||
selectReason,
|
||||
@@ -16,10 +16,10 @@ import {
|
||||
selectIsInvalidIdentity,
|
||||
selectIsVerifyingIdentity,
|
||||
} from '../../machines/bleShare/commonSelectors';
|
||||
import { ScanEvents } from '../../machines/bleShare/scan/scanMachine';
|
||||
import {ScanEvents} from '../../machines/bleShare/scan/scanMachine';
|
||||
|
||||
export function useSendVcScreen() {
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const scanService = appService.children.get('scan');
|
||||
const vcService = appService.children.get('vc');
|
||||
|
||||
@@ -32,9 +32,10 @@ export function useSendVcScreen() {
|
||||
TOGGLE_USER_CONSENT: () =>
|
||||
scanService.send(ScanEvents.TOGGLE_USER_CONSENT()),
|
||||
SELECT_VC_ITEM:
|
||||
(index: number) => (vcRef: ActorRefFrom<typeof vcItemMachine>) => {
|
||||
(index: number) =>
|
||||
(vcRef: ActorRefFrom<typeof ExistingMosipVCItemMachine>) => {
|
||||
setSelectedIndex(index);
|
||||
const { serviceRefs, ...vcData } = vcRef.getSnapshot().context;
|
||||
const {serviceRefs, ...vcData} = vcRef.getSnapshot().context;
|
||||
scanService.send(ScanEvents.SELECT_VC(vcData));
|
||||
},
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import {Centered, Column, Text} from '../../components/ui';
|
||||
import {Icon} from 'react-native-elements';
|
||||
import {Theme} from '../../components/ui/styleUtils';
|
||||
import {Modal} from '../../components/ui/Modal';
|
||||
import {VcItem} from '../../components/VcItem';
|
||||
import {ExistingMosipVCItem} from '../../components/VC/ExistingMosipVCItem/ExistingMosipVCItem';
|
||||
import {ViewVcModal} from '../Home/ViewVcModal';
|
||||
|
||||
export const ReceivedCardsModal: React.FC<ReceivedCardsProps> = ({
|
||||
@@ -31,7 +31,7 @@ export const ReceivedCardsModal: React.FC<ReceivedCardsProps> = ({
|
||||
/>
|
||||
}>
|
||||
{controller.receivedVcsMetadata.map(vcMetadata => (
|
||||
<VcItem
|
||||
<ExistingMosipVCItem
|
||||
key={vcMetadata.getVcKey()}
|
||||
vcMetadata={vcMetadata}
|
||||
margin="0 2 8 2"
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { useSelector } from '@xstate/react';
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import {useSelector} from '@xstate/react';
|
||||
import {useContext, useEffect, useState} from 'react';
|
||||
import NetInfo from '@react-native-community/netinfo';
|
||||
import { GlobalContext } from '../../shared/GlobalContext';
|
||||
import {GlobalContext} from '../../shared/GlobalContext';
|
||||
import {
|
||||
selectIsRefreshingMyVcs,
|
||||
selectMyVcsMetadata,
|
||||
VcEvents,
|
||||
} from '../../machines/vc';
|
||||
import { vcItemMachine } from '../../machines/vcItem';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {ExistingMosipVCItemMachine} from '../../machines/VCItemMachine/ExistingMosipVCItem/ExistingMosipVCItemMachine';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
|
||||
import {
|
||||
RevokeVidsEvents,
|
||||
@@ -17,11 +17,11 @@ import {
|
||||
selectIsLoggingRevoke,
|
||||
} from '../../machines/revoke';
|
||||
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import {ActorRefFrom} from 'xstate';
|
||||
|
||||
export function useRevoke() {
|
||||
const { t } = useTranslation('ProfileScreen');
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const {t} = useTranslation('ProfileScreen');
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const vcService = appService.children.get('vc');
|
||||
const revokeService = appService.children.get('RevokeVids');
|
||||
const vcsMetadata = useSelector(vcService, selectMyVcsMetadata);
|
||||
@@ -29,7 +29,7 @@ export function useRevoke() {
|
||||
const isLoggingRevoke = useSelector(revokeService, selectIsLoggingRevoke);
|
||||
const isAcceptingOtpInput = useSelector(
|
||||
revokeService,
|
||||
selectIsAcceptingOtpInput
|
||||
selectIsAcceptingOtpInput,
|
||||
);
|
||||
|
||||
const [isRevoking, setRevoking] = useState(false);
|
||||
@@ -39,11 +39,11 @@ export function useRevoke() {
|
||||
const [message, setMessage] = useState('');
|
||||
const [selectedIndex, setSelectedIndex] = useState<number>(null);
|
||||
const [selectedVidUniqueIds, setSelectedVidUniqueIds] = useState<string[]>(
|
||||
[]
|
||||
[],
|
||||
);
|
||||
|
||||
const vidsMetadata = vcsMetadata.filter(
|
||||
(vcMetadata) => vcMetadata.idType === 'VID'
|
||||
vcMetadata => vcMetadata.idType === 'VID',
|
||||
);
|
||||
|
||||
const selectVcItem = (index: number, vcUniqueId: string) => {
|
||||
@@ -51,10 +51,10 @@ export function useRevoke() {
|
||||
setSelectedIndex(index);
|
||||
if (selectedVidUniqueIds.includes(vcUniqueId)) {
|
||||
setSelectedVidUniqueIds(
|
||||
selectedVidUniqueIds.filter((item) => item !== vcUniqueId)
|
||||
selectedVidUniqueIds.filter(item => item !== vcUniqueId),
|
||||
);
|
||||
} else {
|
||||
setSelectedVidUniqueIds((prevArray) => [...prevArray, vcUniqueId]);
|
||||
setSelectedVidUniqueIds(prevArray => [...prevArray, vcUniqueId]);
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -91,7 +91,7 @@ export function useRevoke() {
|
||||
selectedVidUniqueIds,
|
||||
toastVisible,
|
||||
uniqueVidsMetadata: vidsMetadata.filter(
|
||||
(vcMetadata, index, vid) => vid.indexOf(vcMetadata) === index
|
||||
(vcMetadata, index, vid) => vid.indexOf(vcMetadata) === index,
|
||||
),
|
||||
|
||||
CONFIRM_REVOKE_VC: () => {
|
||||
@@ -110,7 +110,7 @@ export function useRevoke() {
|
||||
setIsViewing(false);
|
||||
},
|
||||
revokeVc: (otp: string) => {
|
||||
NetInfo.fetch().then((state) => {
|
||||
NetInfo.fetch().then(state => {
|
||||
if (state.isConnected) {
|
||||
revokeService.send(RevokeVidsEvents.INPUT_OTP(otp));
|
||||
} else {
|
||||
@@ -127,5 +127,5 @@ export function useRevoke() {
|
||||
}
|
||||
|
||||
export interface RevokeProps {
|
||||
service: ActorRefFrom<typeof vcItemMachine>;
|
||||
service: ActorRefFrom<typeof ExistingMosipVCItemMachine>;
|
||||
}
|
||||
|
||||
@@ -8,26 +8,47 @@ export class VCMetadata {
|
||||
requestId = '';
|
||||
isPinned = false;
|
||||
id: string = '';
|
||||
|
||||
issuer?: string = '';
|
||||
protocol?: string = '';
|
||||
static vcKeyRegExp = new RegExp(VC_ITEM_STORE_KEY_REGEX);
|
||||
|
||||
constructor({idType = '', requestId = '', isPinned = false, id = ''} = {}) {
|
||||
constructor({
|
||||
idType = '',
|
||||
requestId = '',
|
||||
isPinned = false,
|
||||
id = '',
|
||||
issuer = '',
|
||||
protocol = '',
|
||||
} = {}) {
|
||||
this.idType = idType;
|
||||
this.requestId = requestId;
|
||||
this.isPinned = isPinned;
|
||||
|
||||
this.id = id;
|
||||
|
||||
this.protocol = protocol;
|
||||
this.issuer = issuer;
|
||||
}
|
||||
|
||||
static fromVC(vc: Partial<VC>) {
|
||||
//TODO: Remove any typing and use appropriate typing
|
||||
static fromVC(vc: Partial<VC> | VCMetadata | any) {
|
||||
return new VCMetadata({
|
||||
idType: vc.idType,
|
||||
requestId: vc.requestId,
|
||||
isPinned: vc.isPinned || false,
|
||||
|
||||
id: vc.id,
|
||||
|
||||
protocol: vc.protocol,
|
||||
issuer: vc.issuer,
|
||||
});
|
||||
}
|
||||
|
||||
static fromVcMetadataString(vcMetadataStr: string) {
|
||||
try {
|
||||
if (typeof vcMetadataStr === 'object')
|
||||
return new VCMetadata(vcMetadataStr);
|
||||
return new VCMetadata(JSON.parse(vcMetadataStr));
|
||||
} catch (e) {
|
||||
console.error('Failed to parse VC Metadata', e);
|
||||
@@ -36,12 +57,24 @@ export class VCMetadata {
|
||||
}
|
||||
|
||||
static isVCKey(key: string): boolean {
|
||||
return VCMetadata.vcKeyRegExp.exec(key) != null;
|
||||
//TODO: Check for VC downloaded via esignet as well
|
||||
const [issuer, protocol, id] = key.split(':');
|
||||
|
||||
return (
|
||||
key.startsWith('ESignet') || VCMetadata.vcKeyRegExp.exec(key) != null
|
||||
);
|
||||
}
|
||||
|
||||
isFromOpenId4VCI() {
|
||||
return this.protocol !== '' && this.issuer !== '';
|
||||
}
|
||||
|
||||
// Used for mmkv storage purposes and as a key for components and vc maps
|
||||
// Update VC_ITEM_STORE_KEY_REGEX in case of changes in vckey
|
||||
getVcKey(): string {
|
||||
// openid for vc -> issuer:protocol:vcID
|
||||
//TODO: Separators for VC key to be maintained consistently
|
||||
if (this.protocol) return `${this.issuer}:${this.protocol}:${this.id}`;
|
||||
return `${VC_KEY_PREFIX}_${this.requestId}`;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { KeyPair, RSA } from 'react-native-rsa-native';
|
||||
import {KeyPair, RSA} from 'react-native-rsa-native';
|
||||
import forge from 'node-forge';
|
||||
import getAllConfigurations from '../commonprops/commonProps';
|
||||
import { isIOS } from '../constants';
|
||||
import {isIOS} from '../constants';
|
||||
import SecureKeystore from 'react-native-secure-keystore';
|
||||
import Storage from '../storage';
|
||||
import CryptoJS from 'crypto-js';
|
||||
@@ -21,7 +21,7 @@ export function generateKeys(): Promise<KeyPair> {
|
||||
export async function getJwt(
|
||||
privateKey: string,
|
||||
individualId: string,
|
||||
thumbprint: string
|
||||
thumbprint: string,
|
||||
) {
|
||||
try {
|
||||
var iat = Math.floor(new Date().getTime() / 1000);
|
||||
@@ -30,7 +30,7 @@ export async function getJwt(
|
||||
var config = await getAllConfigurations();
|
||||
|
||||
const header = {
|
||||
'alg': 'RS256',
|
||||
alg: 'RS256',
|
||||
//'kid': keyId,
|
||||
'x5t#S256': thumbprint,
|
||||
};
|
||||
@@ -53,7 +53,7 @@ export async function getJwt(
|
||||
const signature64 = await createSignature(
|
||||
privateKey,
|
||||
preHash,
|
||||
individualId
|
||||
individualId,
|
||||
);
|
||||
|
||||
return header64 + '.' + payload64 + '.' + signature64;
|
||||
@@ -63,10 +63,10 @@ export async function getJwt(
|
||||
}
|
||||
}
|
||||
|
||||
async function createSignature(
|
||||
export async function createSignature(
|
||||
privateKey: string,
|
||||
preHash: string,
|
||||
individualId: string
|
||||
individualId: string,
|
||||
) {
|
||||
let signature64;
|
||||
|
||||
@@ -93,7 +93,7 @@ function replaceCharactersInB64(encodedB64) {
|
||||
return encodedB64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
||||
}
|
||||
|
||||
function encodeB64(str: string) {
|
||||
export function encodeB64(str: string) {
|
||||
const encodedB64 = forge.util.encode64(str);
|
||||
return replaceCharactersInB64(encodedB64);
|
||||
}
|
||||
@@ -124,7 +124,7 @@ export async function clear() {
|
||||
|
||||
export async function encryptJson(
|
||||
encryptionKey: string,
|
||||
data: string
|
||||
data: string,
|
||||
): Promise<string> {
|
||||
if (!isCustomSecureKeystore()) {
|
||||
return CryptoJS.AES.encrypt(data, encryptionKey).toString();
|
||||
@@ -134,12 +134,12 @@ export async function encryptJson(
|
||||
|
||||
export async function decryptJson(
|
||||
encryptionKey: string,
|
||||
encryptedData: string
|
||||
encryptedData: string,
|
||||
): Promise<string> {
|
||||
try {
|
||||
if (!isCustomSecureKeystore()) {
|
||||
return CryptoJS.AES.decrypt(encryptedData, encryptionKey).toString(
|
||||
CryptoJS.enc.Utf8
|
||||
CryptoJS.enc.Utf8,
|
||||
);
|
||||
}
|
||||
return await SecureKeystore.decryptData(ENCRYPTION_ID, encryptedData);
|
||||
|
||||
86
shared/openId4VCI/Utils.ts
Normal file
86
shared/openId4VCI/Utils.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import {ENABLE_OPENID_FOR_VC} from 'react-native-dotenv';
|
||||
import {createSignature, encodeB64} from '../cryptoutil/cryptoUtil';
|
||||
import jwtDecode from 'jwt-decode';
|
||||
import jose from 'node-jose';
|
||||
import {VCMetadata} from '../VCMetadata';
|
||||
|
||||
export const OpenId4VCIProtocol = 'OpenId4VCI';
|
||||
export const isVCFromOpenId4VCI = (vcMetadata: VCMetadata) => {
|
||||
return vcMetadata.isFromOpenId4VCI();
|
||||
};
|
||||
|
||||
export const isOpenId4VCIEnabled = () => {
|
||||
return ENABLE_OPENID_FOR_VC === 'true';
|
||||
};
|
||||
|
||||
export const getIdentifier = (context, credential) => {
|
||||
const credId = credential.credential.id.split('/');
|
||||
return (
|
||||
context.selectedIssuer.id +
|
||||
':' +
|
||||
context.selectedIssuer.protocol +
|
||||
':' +
|
||||
credId[credId.length - 1]
|
||||
);
|
||||
};
|
||||
|
||||
export const getBody = async context => {
|
||||
const proofJWT = await getJWT(context);
|
||||
return {
|
||||
format: 'ldp_vc',
|
||||
credential_definition: {
|
||||
'@context': ['https://www.w3.org/2018/credentials/v1'],
|
||||
type: ['VerifiableCredential', 'MOSIPVerifiableCredential'],
|
||||
},
|
||||
proof: {
|
||||
proof_type: 'jwt',
|
||||
jwt: proofJWT,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const getJWK = async publicKey => {
|
||||
try {
|
||||
const publicKeyJWKString = await jose.JWK.asKey(publicKey, 'pem');
|
||||
const publicKeyJWK = publicKeyJWKString.toJSON();
|
||||
return {
|
||||
...publicKeyJWK,
|
||||
alg: 'RS256',
|
||||
use: 'sig',
|
||||
};
|
||||
} catch (e) {
|
||||
console.log(
|
||||
'Exception occured while constructing JWK from PEM : ' +
|
||||
publicKey +
|
||||
' Exception is ',
|
||||
e,
|
||||
);
|
||||
}
|
||||
};
|
||||
export const getJWT = async context => {
|
||||
try {
|
||||
const header64 = encodeB64(
|
||||
JSON.stringify({
|
||||
alg: 'RS256',
|
||||
jwk: await getJWK(context.publicKey),
|
||||
typ: 'openid4vci-proof+jwt',
|
||||
}),
|
||||
);
|
||||
const decodedToken = jwtDecode(context.tokenResponse.accessToken);
|
||||
const payload64 = encodeB64(
|
||||
JSON.stringify({
|
||||
iss: context.selectedIssuer.clientId,
|
||||
nonce: decodedToken.c_nonce,
|
||||
aud: 'https://esignet.dev1.mosip.net/v1/esignet',
|
||||
iat: Math.floor(new Date().getTime() / 1000),
|
||||
exp: Math.floor(new Date().getTime() / 1000) + 18000,
|
||||
}),
|
||||
);
|
||||
const preHash = header64 + '.' + payload64;
|
||||
const signature64 = await createSignature(context.privateKey, preHash, '');
|
||||
return header64 + '.' + payload64 + '.' + signature64;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
@@ -1,10 +1,11 @@
|
||||
import vcjs from '@digitalcredentials/vc';
|
||||
import jsonld from '@digitalcredentials/jsonld';
|
||||
import { RsaSignature2018 } from '../../lib/jsonld-signatures/suites/rsa2018/RsaSignature2018';
|
||||
import { Ed25519Signature2018 } from '../../lib/jsonld-signatures/suites/ed255192018/Ed25519Signature2018';
|
||||
import { AssertionProofPurpose } from '../../lib/jsonld-signatures/purposes/AssertionProofPurpose';
|
||||
import { PublicKeyProofPurpose } from '../../lib/jsonld-signatures/purposes/PublicKeyProofPurpose';
|
||||
import { VerifiableCredential } from '../../types/vc';
|
||||
import {RsaSignature2018} from '../../lib/jsonld-signatures/suites/rsa2018/RsaSignature2018';
|
||||
import {Ed25519Signature2018} from '../../lib/jsonld-signatures/suites/ed255192018/Ed25519Signature2018';
|
||||
import {AssertionProofPurpose} from '../../lib/jsonld-signatures/purposes/AssertionProofPurpose';
|
||||
import {PublicKeyProofPurpose} from '../../lib/jsonld-signatures/purposes/PublicKeyProofPurpose';
|
||||
import {VerifiableCredential} from '../../types/vc';
|
||||
import {Credential} from '../../components/VC copy/EsignetMosipVCItem/vc';
|
||||
|
||||
// FIXME: Ed25519Signature2018 not fully supported yet.
|
||||
const ProofType = {
|
||||
@@ -18,7 +19,7 @@ const ProofPurpose = {
|
||||
};
|
||||
|
||||
export async function verifyCredential(
|
||||
verifiableCredential: VerifiableCredential
|
||||
verifiableCredential: VerifiableCredential | Credential,
|
||||
): Promise<boolean> {
|
||||
let purpose: PublicKeyProofPurpose | AssertionProofPurpose;
|
||||
switch (verifiableCredential.proof.proofPurpose) {
|
||||
|
||||
5
types/react-native-dotenv/index.d.ts
vendored
5
types/react-native-dotenv/index.d.ts
vendored
@@ -29,6 +29,11 @@ declare module 'react-native-dotenv' {
|
||||
*/
|
||||
export const CREDENTIAL_REGISTRY_EDIT: string;
|
||||
|
||||
/**
|
||||
* Flag for Toggling Download via UIN/VID
|
||||
*/
|
||||
export const ENABLE_OPENID_FOR_VC: string;
|
||||
|
||||
/**
|
||||
* LANGUAGE for the unsupported device languages
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user