new ui changes

This commit is contained in:
srikanth716
2023-03-10 19:54:41 +05:30
committed by Sri Kanth Kola
parent 52ff018f49
commit ac7dd03e2b
30 changed files with 1192 additions and 1680 deletions

View File

@@ -1,65 +1,70 @@
import React, { useRef, useState } from 'react';
import React, { useState } from 'react';
import { t } from 'i18next';
import { BottomSheet, Icon, ListItem } from 'react-native-elements';
import { Theme } from '../components/ui/styleUtils';
import { Centered, Column, Row, Text } from '../components/ui';
import { WalletBinding } from '../screens/Home/MyVcs/WalletBinding';
import { Pressable } from 'react-native';
import { useKebabPopUp } from './KebabPopUpController';
export const KebabPopUpMenu: React.FC<KebabPopUpMenuProps> = (props) => {
export const KebabPopUp: React.FC<KebabPopUpProps> = (props) => {
const controller = useKebabPopUp(props);
return (
<BottomSheet
isVisible={props.isVisible}
containerStyle={{
backgroundColor: 'transparent',
elevation: 4,
marginHorizontal: 4,
borderTopLeftRadius: 30,
borderTopRightRadius: 30,
}}>
<Row
style={{
backgroundColor: 'white',
borderTopLeftRadius: 30,
borderTopRightRadius: 24,
padding: 18,
justifyContent: 'space-between',
}}>
<Centered></Centered>
<Text
weight="bold"
style={{ ...Theme.TextStyles.base, flex: 1, alignSelf: 'center' }}>
{t('More Options')}
</Text>
<Column>
<Pressable onPress={() => controller.setVisible(true)}>
<Icon
name="close"
onPress={props.close}
color={Theme.Colors.Details}
size={25}
name={props.iconName}
type={props.iconType}
color={Theme.Colors.GrayIcon}
/>
</Row>
<Column>
<ListItem bottomDivider>
<ListItem.Content>
<ListItem.Title>
<Text size="small" style={Theme.TextStyles.bold}>
{t('Unpin Card')}
</Text>
</ListItem.Title>
</ListItem.Content>
</ListItem>
</Pressable>
<BottomSheet
isVisible={controller.visible}
containerStyle={Theme.KebabPopUpStyles.kebabPopUp}>
<Row style={Theme.KebabPopUpStyles.kebabHeaderStyle}>
<Centered></Centered>
<Text
weight="bold"
style={{ ...Theme.TextStyles.base, flex: 1, alignSelf: 'center' }}>
{t('More Options')}
</Text>
<Icon
name="close"
onPress={() => controller.setVisible(false)}
color={Theme.Colors.Details}
size={25}
/>
</Row>
<Column>
<ListItem bottomDivider>
<ListItem.Content>
<ListItem.Title>
<Pressable onPress={controller.PIN_CARD}>
<Text size="small" weight="bold">
{props.vcKey.split(':')[4] == 'true'
? t('Unpin Card')
: t('Pin Card')}
</Text>
</Pressable>
</ListItem.Title>
</ListItem.Content>
</ListItem>
<WalletBinding
label={t('Offline authentication disabled!')}
Content={t(
'Click here to enable the credentials to be used for offline authentication.'
)}
/>
</Column>
</BottomSheet>
<WalletBinding
label={t('Offline authentication disabled!')}
Content={t(
'Click here to enable the credentials to be used for offline authentication.'
)}
vcKey={props.vcKey}
/>
</Column>
</BottomSheet>
</Column>
);
};
interface KebabPopUpMenuProps {
isVisible: boolean;
close: () => void;
interface KebabPopUpProps {
iconName: string;
iconType?: string;
vcKey: string;
}

View File

@@ -0,0 +1,67 @@
import { useInterpret, useSelector } from '@xstate/react';
import { useContext, useRef, useState } from 'react';
import {
createVcItemMachine,
isShowBindingWarning,
isWalletBindingInProgress,
selectAcceptingBindingOtp,
selectEmptyWalletBindingId,
selectIsPinned,
selectOtpError,
selectShowWalletBindingError,
selectWalletBindingError,
VcItemEvents,
} from '../machines/vcItem';
import { GlobalContext } from '../shared/GlobalContext';
export function useKebabPopUp(props) {
const { appService } = useContext(GlobalContext);
const machine = useRef(
createVcItemMachine(
appService.getSnapshot().context.serviceRefs,
props.vcKey
)
);
const service = useInterpret(machine.current, { devTools: __DEV__ });
const PIN_CARD = () => service.send(VcItemEvents.PIN_CARD());
const ADD_WALLET_BINDING_ID = () =>
service.send(VcItemEvents.ADD_WALLET_BINDING_ID());
const CONFIRM = () => service.send(VcItemEvents.CONFIRM());
const DISMISS = () => service.send(VcItemEvents.DISMISS());
const CANCEL = () => service.send(VcItemEvents.CANCEL());
const INPUT_OTP = (otp: string) => service.send(VcItemEvents.INPUT_OTP(otp));
const isPinned = useSelector(service, selectIsPinned);
const [visible, setVisible] = useState(false);
const isBindingWarning = useSelector(service, isShowBindingWarning);
const isAcceptingOtpInput = useSelector(service, selectAcceptingBindingOtp);
const isWalletBindingError = useSelector(
service,
selectShowWalletBindingError
);
const otpError = useSelector(service, selectOtpError);
const walletBindingError = useSelector(service, selectWalletBindingError);
const WalletBindingInProgress = useSelector(
service,
isWalletBindingInProgress
);
const emptyWalletBindingId = useSelector(service, selectEmptyWalletBindingId);
return {
isPinned,
visible,
setVisible,
PIN_CARD,
ADD_WALLET_BINDING_ID,
CONFIRM,
DISMISS,
CANCEL,
INPUT_OTP,
isBindingWarning,
isAcceptingOtpInput,
isWalletBindingError,
walletBindingError,
otpError,
WalletBindingInProgress,
emptyWalletBindingId,
};
}

View File

@@ -1,4 +1,4 @@
import React, { useContext, useRef } from 'react';
import React, { useContext, useRef, useState } from 'react';
import { useInterpret, useSelector } from '@xstate/react';
import {
Pressable,
@@ -7,6 +7,13 @@ import {
Dimensions,
View,
} from 'react-native';
import {
Pressable,
Image,
ImageBackground,
Dimensions,
View,
} from 'react-native';
import { CheckBox, Icon } from 'react-native-elements';
import { ActorRefFrom } from 'xstate';
import {
@@ -20,19 +27,18 @@ import {
} from '../machines/vcItem';
import { Column, Row, Text } from './ui';
import { Theme } from './ui/styleUtils';
import { RotatingIcon } from './RotatingIcon';
import { GlobalContext } from '../shared/GlobalContext';
import { useTranslation } from 'react-i18next';
import { LocalizedField } from '../types/vc';
import { VcItemTags } from './VcItemTags';
import VerifiedIcon from './VerifiedIcon';
import { KebabPopUp } from './KebabPopUp';
const getDetails = (arg1, arg2, verifiableCredential) => {
if (arg1 === 'Status') {
return (
<Column>
<Text
weight="bold"
weight="regular"
size="smaller"
color={
!verifiableCredential
@@ -44,7 +50,7 @@ const getDetails = (arg1, arg2, verifiableCredential) => {
<Row>
<Text
color={Theme.Colors.Details}
weight="bold"
weight="semibold"
size="smaller"
style={
!verifiableCredential
@@ -57,33 +63,6 @@ const getDetails = (arg1, arg2, verifiableCredential) => {
</Row>
</Column>
);
} else if (arg1 === 'Full name') {
return (
<Column padding="0 200 0 0" margin="0 10 0 0">
<Text
color={
!verifiableCredential
? Theme.Colors.LoadingDetailsLabel
: Theme.Colors.DetailsLabel
}
weight="bold"
size="smaller">
{arg1}
</Text>
<Text
numLines={4}
color={Theme.Colors.Details}
weight="bold"
size="smaller"
style={
!verifiableCredential
? Theme.Styles.loadingTitle
: Theme.Styles.subtitle
}>
{!verifiableCredential ? '' : arg2}
</Text>
</Column>
);
} else {
return (
<Column>
@@ -93,15 +72,14 @@ const getDetails = (arg1, arg2, verifiableCredential) => {
? Theme.Colors.LoadingDetailsLabel
: Theme.Colors.DetailsLabel
}
size="smaller"
weight={'bold'}
style={Theme.Styles.vcItemLabelHeader}>
weight="regular"
size="smaller">
{arg1}
</Text>
<Text
numLines={4}
color={Theme.Colors.Details}
weight="bold"
weight="semibold"
size="smaller"
style={
!verifiableCredential
@@ -172,7 +150,6 @@ export const VcItem: React.FC<VcItemProps> = (props) => {
) : null;
const tag = useSelector(service, selectTag);
return (
<Pressable
onPress={() => props.onPress(service)}
@@ -191,143 +168,146 @@ export const VcItem: React.FC<VcItemProps> = (props) => {
? Theme.Styles.vertloadingContainer
: Theme.Styles.backgroundImageContainer
}>
{!verifiableCredential ? (
<Column align="space-between" crossAlign="center" padding="50">
<RotatingIcon name="sync" color={Theme.Colors.Icon} />
<Text margin="20 0 0 0">{t('downloading')}</Text>
</Column>
) : (
<Column>
<Row align="space-between">
<Row>
<Image
source={{ uri: context.credential.biometrics.face }}
style={Theme.Styles.closeCardImage}
/>
<Column margin="0 0 0 10">
{getDetails(t('fullName'), fullName, verifiableCredential)}
<Column margin="10 0 0 0">
<Text
color={Theme.Colors.DetailsLabel}
weight="bold"
size="smaller"
align="left">
{t('idType')}
</Text>
<Text
weight="bold"
color={Theme.Colors.Details}
size="smaller"
style={Theme.Styles.subtitle}>
{t('nationalCard')}
</Text>
</Column>
</Column>
</Row>
<Column>
{verifiableCredential ? (
selectableOrCheck
) : (
<RotatingIcon name="sync" color={Theme.Colors.rotatingIcon} />
)}
</Column>
</Row>
<Row align="space-between" margin="5 0 0 0">
<Column>
{uin ? getDetails(t('uin'), uin, verifiableCredential) : null}
{vid ? getDetails(t('vid'), vid, verifiableCredential) : null}
{getDetails(
t('generatedOn'),
generatedOn,
verifiableCredential
)}
</Column>
<Column>
{getDetails(t('status'), isvalid, verifiableCredential)}
</Column>
<Column style={Theme.Styles.closecardMosipLogo}>
<Image
source={Theme.MosipLogo}
style={Theme.Styles.logo}
resizeMethod="auto"
/>
</Column>
</Row>
</Column>
)}
<VcItemTags tag={tag} />
</ImageBackground>
{props.activeTab !== 'receivedVcsTab' &&
props.activeTab != 'sharingVcScreen' && (
<Row>
{emptyWalletBindingId ? (
<Row
width={Dimensions.get('screen').width * 0.8}
align="space-between"
crossAlign="center">
<Row crossAlign="center" style={{ flex: 1 }}>
{verifiableCredential && <WalletUnverified />}
<Text
color={Theme.Colors.Details}
weight="semibold"
size="small"
margin="10 33 10 10"
style={
!verifiableCredential
? Theme.Styles.loadingTitle
: Theme.Styles.statusLabel
}
children={t('offlineAuthDisabledHeader')}></Text>
</Row>
<Pressable
onPress={() =>
verifiableCredential ? props.onPress(service) : null
}>
<Column>
<Row align="space-between">
<Row>
<ImageBackground
source={
!verifiableCredential
? Theme.ProfileIcon
: { uri: context.credential.biometrics.face }
}
style={Theme.Styles.closeCardImage}>
{props.iconName && (
<Icon
name="dots-three-horizontal"
type="entypo"
color={Theme.Colors.GrayIcon}
name={props.iconName}
type={props.iconType}
color={Theme.Colors.Icon}
style={{ marginLeft: -80 }}
/>
</Pressable>
</Row>
) : (
<Row
width={Dimensions.get('screen').width * 0.8}
align="space-between"
crossAlign="center">
<Row crossAlign="center" style={{ flex: 1 }}>
<WalletVerified />
)}
</ImageBackground>
<Column margin="0 0 0 10">
{getDetails(t('fullName'), fullName, verifiableCredential)}
<Column margin="10 0 0 0">
<Text
color={Theme.Colors.statusLabel}
color={
!verifiableCredential
? Theme.Colors.LoadingDetailsLabel
: Theme.Colors.DetailsLabel
}
weight="semibold"
size="smaller"
margin="10 10 10 10"
align="left">
{t('idType')}
</Text>
<Text
weight="regular"
color={Theme.Colors.Details}
size="smaller"
style={
!verifiableCredential
? Theme.Styles.loadingTitle
: Theme.Styles.subtitle
}
children={t('profileAuthenticated')}></Text>
</Row>
}>
{t('nationalCard')}
</Text>
</Column>
</Column>
</Row>
{props.showOnlyBindedVc ? null : (
<Pressable onPress={() => props.onPress(service)}>
<Icon
name="dots-three-horizontal"
type="entypo"
color={Theme.Colors.GrayIcon}
/>
</Pressable>
)}
</Row>
<Column>{verifiableCredential ? selectableOrCheck : null}</Column>
</Row>
<Row
align="space-between"
margin="5 0 0 0"
style={
!verifiableCredential ? Theme.Styles.loadingContainer : null
}>
<Column>
{uin ? getDetails(t('uin'), uin, verifiableCredential) : null}
{vid ? getDetails(t('vid'), vid, verifiableCredential) : null}
{!verifiableCredential
? getDetails(t('id'), uin || vid, verifiableCredential)
: null}
{getDetails(t('generatedOn'), generatedOn, verifiableCredential)}
</Column>
<Column>
{verifiableCredential
? getDetails(t('status'), isvalid, verifiableCredential)
: null}
</Column>
<Column style={Theme.Styles.closecardMosipLogo}>
<Image
source={Theme.MosipLogo}
style={Theme.Styles.logo}
resizeMethod="auto"
/>
</Column>
</Row>
</Column>
<VcItemTags tag={tag} />
</ImageBackground>
<Row>
{emptyWalletBindingId ? (
<Row
width={Dimensions.get('screen').width * 0.8}
align="space-between"
crossAlign="center">
<Row crossAlign="center" style={{ flex: 1 }}>
{verifiableCredential && <WalletUnverified />}
<Text
color={Theme.Colors.Details}
weight="semibold"
size="small"
margin="10 33 10 10"
style={
!verifiableCredential
? Theme.Styles.loadingTitle
: Theme.Styles.subtitle
}
children={t('offlineAuthDisabledHeader')}></Text>
</Row>
<KebabPopUp
vcKey={props.vcKey}
iconName="dots-three-horizontal"
iconType="entypo"
/>
</Row>
) : (
<Row
width={Dimensions.get('screen').width * 0.8}
align="space-between"
crossAlign="center">
<Row crossAlign="center" style={{ flex: 1 }}>
<WalletVerified />
<Text
color={Theme.Colors.Details}
weight="semibold"
size="smaller"
margin="10 10 10 10"
style={
!verifiableCredential
? Theme.Styles.loadingTitle
: Theme.Styles.subtitle
}
children={t('profileAuthenticated')}></Text>
</Row>
{props.showOnlyBindedVc ? null : (
<Pressable>
<KebabPopUp
vcKey={props.vcKey}
iconName="dots-three-horizontal"
iconType="entypo"
/>
</Pressable>
)}
</Row>
)}
</Row>
</Pressable>
);
};
@@ -340,7 +320,8 @@ interface VcItemProps {
showOnlyBindedVc?: boolean;
onPress?: (vcRef?: ActorRefFrom<typeof vcItemMachine>) => void;
onShow?: (vcRef?: ActorRefFrom<typeof vcItemMachine>) => void;
activeTab?: string;
iconName?: string;
iconType?: string;
}
function getLocalizedField(rawField: string | LocalizedField) {

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { I18nManager, Modal as RNModal, View } from 'react-native';
import { Icon } from 'react-native-elements';
import { Column, Row, Text } from '.';
import { Centered, Column, Row, Text } from '.';
import { ElevationLevel, Theme } from './styleUtils';
export const Modal: React.FC<ModalProps> = (props) => {
@@ -37,9 +37,9 @@ export const Modal: React.FC<ModalProps> = (props) => {
color={Theme.Colors.Details}
/>
) : null}
<Row fill align="center" margin={'5 30 0 0'}>
<Centered fill align="center">
<Text weight="semibold">{props.headerTitle}</Text>
</Row>
</Centered>
{props.headerRight || props.arrowLeft || (
<Icon
name="close"

View File

@@ -20,6 +20,8 @@ const Colors = {
GrayText: '#6F6F6F',
dorColor: '#CBCBCB',
plainText: '#FFD6A7',
walletbindingLabel: '#000000',
walletbindingContent: '#666666',
};
export type ElevationLevel = 0 | 1 | 2 | 3 | 4 | 5;
@@ -72,6 +74,8 @@ export const DefaultTheme = {
gradientBtn: ['#F59B4B', '#E86E04'],
dotColor: Colors.dorColor,
plainText: Colors.plainText,
walletbindingLabel: Colors.walletbindingLabel,
walletbindingContent: Colors.walletbindingContent,
},
Styles: StyleSheet.create({
title: {
@@ -298,7 +302,7 @@ export const DefaultTheme = {
detailsText: {
fontWeight: 'bold',
fontSize: 15,
fontFamily: 'Poppins_700Bold',
fontFamily: 'Inter_700Bold',
},
getId: {
justifyContent: 'center',
@@ -306,7 +310,7 @@ export const DefaultTheme = {
marginTop: 10,
},
placeholder: {
fontFamily: 'Poppins_400Regular',
fontFamily: 'Inter_400Regular',
},
hrLine: {
borderBottomColor: 'black',
@@ -320,7 +324,7 @@ export const DefaultTheme = {
borderColor: Colors.Grey,
color: Colors.Black,
flex: 1,
fontFamily: 'Poppins_600SemiBold',
fontFamily: 'Inter_600SemiBold',
fontSize: 18,
fontWeight: '600',
height: 40,
@@ -336,13 +340,13 @@ export const DefaultTheme = {
lineHeight: 28,
},
regular: {
fontFamily: 'Poppins_400Regular',
fontFamily: 'Inter_400Regular',
},
semibold: {
fontFamily: 'Poppins_600SemiBold',
fontFamily: 'Inter_600SemiBold',
},
bold: {
fontFamily: 'Poppins_700Bold',
fontFamily: 'Inter_700Bold',
},
small: {
fontSize: 14,
@@ -511,12 +515,25 @@ export const DefaultTheme = {
height: Dimensions.get('screen').height,
},
}),
KebabPopUpStyles: StyleSheet.create({
kebabPopUp: {
marginHorizontal: 4,
},
kebabHeaderStyle: {
backgroundColor: 'white',
justifyContent: 'space-between',
borderTopLeftRadius: 30,
borderTopRightRadius: 24,
padding: 18,
},
}),
MessageOverlayStyles: StyleSheet.create({
overlay: {
elevation: 5,
backgroundColor: Colors.White,
padding: 0,
},
button: {
borderTopLeftRadius: 0,
borderTopRightRadius: 0,
@@ -678,7 +695,7 @@ export const DefaultTheme = {
sliderTitle: {
color: Colors.White,
marginBottom: 20,
fontFamily: 'Poppins_700Bold',
fontFamily: 'Inter_700Bold',
},
text: {
color: Colors.White,

View File

@@ -92,6 +92,7 @@
"historyTab": "History"
},
"AddVcModal": {
"inputIdHeader":"Retrieve your ID",
"requestingCredential": "Requesting credential...",
"errors": {
"input": {
@@ -140,8 +141,8 @@
"qstnMarkToolTip": "Application ID is available in the acknowledgement received after enrolment."
},
"IdInputModal": {
"header": "Enter your UIN/VID to download your {{vcLabel}}",
"generateVc": "Generate My {{vcLabel}}",
"header": "Select ID type and enter the MOSIP provided UIN or VID of the ID you wish to retrieve",
"generateVc": "Generate {{vcLabel}}",
"enterId": "Enter your {{idType}}",
"noUIN/VID": "Don't have your UIN/VID? Get it here",
"requestingOTP": "Requesting OTP..."
@@ -159,9 +160,9 @@
"stepOneTitle": "Secure Sharing!",
"stepOneText": "Share and receive {{vcLabel}} switfly using your phone camera to scan QR codes",
"stepTwoTitle": "Trusted Digital Wallet",
"stepTwoText": " Keep your digital credential with you at all times",
"stepTwoText": "Keep your digital credential with you at all times",
"stepThreeTitle": "Quick Access",
"stepThreeText": "Once generated, {{vcLabel}} are safely stored on your mobile and can be renamed or shared at any time.",
"stepThreeText": "Once generated, {{vcLabel}} are safely stored on your mobile.",
"stepThreeButton": "Get Started",
"skip": "Skip",
"next": "Next"

View File

@@ -1,94 +0,0 @@
// This file was automatically generated. Edits will be overwritten
export interface Typegen0 {
'@@xstate/typegen': true;
'internalEvents': {
'done.invoke.walletBinding.addKeyPair:invocation[0]': {
type: 'done.invoke.walletBinding.addKeyPair:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.walletBinding.addingWalletBindingId:invocation[0]': {
type: 'done.invoke.walletBinding.addingWalletBindingId:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.walletBinding.requestingBindingOtp:invocation[0]': {
type: 'done.invoke.walletBinding.requestingBindingOtp:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.walletBinding.updatingPrivateKey:invocation[0]': {
type: 'done.invoke.walletBinding.updatingPrivateKey:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'error.platform.walletBinding.addKeyPair:invocation[0]': {
type: 'error.platform.walletBinding.addKeyPair:invocation[0]';
data: unknown;
};
'error.platform.walletBinding.addingWalletBindingId:invocation[0]': {
type: 'error.platform.walletBinding.addingWalletBindingId:invocation[0]';
data: unknown;
};
'error.platform.walletBinding.requestingBindingOtp:invocation[0]': {
type: 'error.platform.walletBinding.requestingBindingOtp:invocation[0]';
data: unknown;
};
'error.platform.walletBinding.updatingPrivateKey:invocation[0]': {
type: 'error.platform.walletBinding.updatingPrivateKey:invocation[0]';
data: unknown;
};
'xstate.init': { type: 'xstate.init' };
};
'invokeSrcNameMap': {
addWalletBindnigId: 'done.invoke.walletBinding.addingWalletBindingId:invocation[0]';
generateKeyPair: 'done.invoke.walletBinding.addKeyPair:invocation[0]';
requestBindingOtp: 'done.invoke.walletBinding.requestingBindingOtp:invocation[0]';
updatePrivateKey: 'done.invoke.walletBinding.updatingPrivateKey:invocation[0]';
};
'missingImplementations': {
actions: 'clearTransactionId';
delays: never;
guards: never;
services: never;
};
'eventsCausingActions': {
clearOtp:
| 'DISMISS'
| 'done.invoke.walletBinding.requestingBindingOtp:invocation[0]';
clearTransactionId: 'DISMISS';
setOtp: 'INPUT_OTP';
setPrivateKey: 'done.invoke.walletBinding.addKeyPair:invocation[0]';
setPublicKey: 'done.invoke.walletBinding.addKeyPair:invocation[0]';
setWalletBindingError:
| 'error.platform.walletBinding.addKeyPair:invocation[0]'
| 'error.platform.walletBinding.addingWalletBindingId:invocation[0]'
| 'error.platform.walletBinding.requestingBindingOtp:invocation[0]'
| 'error.platform.walletBinding.updatingPrivateKey:invocation[0]';
setWalletBindingErrorEmpty:
| 'CANCEL'
| 'done.invoke.walletBinding.updatingPrivateKey:invocation[0]';
setWalletBindingId: 'done.invoke.walletBinding.addingWalletBindingId:invocation[0]';
storeContext: 'done.invoke.walletBinding.updatingPrivateKey:invocation[0]';
updatePrivateKey: 'done.invoke.walletBinding.updatingPrivateKey:invocation[0]';
updateVc: 'done.invoke.walletBinding.updatingPrivateKey:invocation[0]';
};
'eventsCausingDelays': {};
'eventsCausingGuards': {};
'eventsCausingServices': {
addWalletBindnigId: 'done.invoke.walletBinding.addKeyPair:invocation[0]';
generateKeyPair: 'INPUT_OTP';
requestBindingOtp: 'CONFIRM';
updatePrivateKey: 'done.invoke.walletBinding.addingWalletBindingId:invocation[0]';
};
'matchesStates':
| 'acceptingBindingOtp'
| 'addKeyPair'
| 'addingWalletBindingId'
| 'requestingBindingOtp'
| 'showBindingWarning'
| 'showingWalletBindingError'
| 'updatingPrivateKey';
'tags': never;
}

View File

@@ -124,6 +124,7 @@ export type ActivityLogType =
| 'VC_RECEIVED_NOT_SAVED'
| 'VC_DELETED'
| 'VC_DOWNLOADED'
| 'VC_UPDATED'
| 'VC_REVOKED'
| 'VC_SHARED_WITH_VERIFICATION_CONSENT'
| 'VC_RECEIVED_WITH_PRESENCE_VERIFIED'

View File

@@ -17,7 +17,6 @@ import * as BLERequest from './openIdBle/request';
import * as BLEScan from './openIdBle/scan';
import { createScanMachine, scanMachine } from './scan';
import { createRevokeMachine, revokeVidsMachine } from './revoke';
import { pure, respond } from 'xstate/lib/actions';
import { AppServices } from '../shared/GlobalContext';
import { request } from '../shared/request';
@@ -229,6 +228,7 @@ export const appMachine = model.createMachine(
context.serviceRefs.scan.subscribe(logState);
context.serviceRefs.request.subscribe(logState);
context.serviceRefs.revoke.subscribe(logState);
context.serviceRefs.walletBinding.subscribe(logState);
}
},

View File

@@ -6,6 +6,7 @@ import { EventFrom, Receiver, sendParent, send, sendUpdate } from 'xstate';
import { createModel } from 'xstate/lib/model';
import { generateSecureRandom } from 'react-native-securerandom';
import { log } from 'xstate/lib/actions';
import { VC_ITEM_STORE_KEY } from '../shared/constants';
const ENCRYPTION_ID = 'c7c22a6c-9759-4605-ac88-46f4041d863d';
@@ -21,6 +22,7 @@ const model = createModel(
SET: (key: string, value: unknown) => ({ key, value }),
APPEND: (key: string, value: unknown) => ({ key, value }),
PREPEND: (key: string, value: unknown) => ({ key, value }),
UPDATE: (key: string, value: string) => ({ key, value }),
REMOVE: (key: string, value: string) => ({ key, value }),
REMOVE_ITEMS: (key: string, values: string[]) => ({ key, values }),
CLEAR: () => ({}),
@@ -116,6 +118,9 @@ export const storeMachine =
PREPEND: {
actions: 'forwardStoreRequest',
},
UPDATE: {
actions: 'forwardStoreRequest',
},
REMOVE: {
actions: 'forwardStoreRequest',
},
@@ -205,6 +210,16 @@ export const storeMachine =
response = event.value;
break;
}
case 'UPDATE': {
await updateItem(
event.key,
event.value,
context.encryptionKey
);
response = event.value;
break;
}
case 'REMOVE': {
await removeItem(
event.key,
@@ -341,7 +356,30 @@ export async function prependItem(
throw e;
}
}
export async function updateItem(
key: string,
value: string,
encryptionKey: string
) {
try {
const list = await getItem(key, [], encryptionKey);
const newList = [
value,
...list.map((item) => {
const vc = item.split(':');
if (vc[3] !== value.split(':')[3]) {
vc[4] = 'false';
return vc.join(':');
}
}),
].filter((value) => value != undefined && value !== null);
await setItem(key, newList, encryptionKey);
} catch (e) {
console.error('error prependItem:', e);
throw e;
}
}
export async function removeItem(
key: string,
value: string,

View File

@@ -36,7 +36,8 @@ export interface Typegen0 {
| 'PREPEND'
| 'REMOVE'
| 'REMOVE_ITEMS'
| 'SET';
| 'SET'
| 'UPDATE';
notifyParent:
| 'KEY_RECEIVED'
| 'done.invoke.store.resettingStorage:invocation[0]';

View File

@@ -26,6 +26,7 @@ const model = createModel(
STORE_RESPONSE: (response: unknown) => ({ response }),
STORE_ERROR: (error: Error) => ({ error }),
VC_ADDED: (vcKey: string) => ({ vcKey }),
VC_UPDATED: (vcKey: string) => ({ vcKey }),
VC_RECEIVED: (vcKey: string) => ({ vcKey }),
VC_DOWNLOADED: (vc: VC) => ({ vc }),
REFRESH_MY_VCS: () => ({}),
@@ -133,6 +134,9 @@ export const vcMachine =
VC_ADDED: {
actions: 'prependToMyVcs',
},
VC_UPDATED: {
actions: ['updateMyVcs', 'setUpdateVc'],
},
VC_DOWNLOADED: {
actions: 'setDownloadedVc',
},
@@ -181,10 +185,31 @@ export const vcMachine =
context.vcs[VC_ITEM_STORE_KEY(event.vc)] = event.vc;
},
setUpdateVc: send(
(_context, event) => {
return StoreEvents.UPDATE(MY_VCS_STORE_KEY, event.vcKey);
},
{ to: (context) => context.serviceRefs.store }
),
prependToMyVcs: model.assign({
myVcs: (context, event) => [event.vcKey, ...context.myVcs],
}),
updateMyVcs: model.assign({
myVcs: (context, event) =>
[
event.vcKey,
...context.myVcs.map((value) => {
const vc = value.split(':');
if (vc[3] !== event.vcKey.split(':')[3]) {
vc[4] = 'false';
return vc.join(':');
}
}),
].filter((value) => value != undefined),
}),
prependToReceivedVcs: model.assign({
receivedVcs: (context, event) => [
event.vcKey,

View File

@@ -0,0 +1,56 @@
// This file was automatically generated. Edits will be overwritten
export interface Typegen0 {
'@@xstate/typegen': true;
'internalEvents': {
'xstate.init': { type: 'xstate.init' };
};
'invokeSrcNameMap': {};
'missingImplementations': {
actions: never;
delays: never;
guards: never;
services: never;
};
'eventsCausingActions': {
getReceivedVcsResponse: 'GET_RECEIVED_VCS';
getVcItemResponse: 'GET_VC_ITEM';
loadMyVcs: 'REFRESH_MY_VCS' | 'xstate.init';
loadReceivedVcs: 'REFRESH_RECEIVED_VCS' | 'STORE_RESPONSE';
moveExistingVcToTop: 'VC_RECEIVED';
prependToMyVcs: 'VC_ADDED';
prependToReceivedVcs: 'VC_RECEIVED';
setDownloadedVc: 'VC_DOWNLOADED';
setMyVcs: 'STORE_RESPONSE';
setReceivedVcs: 'STORE_RESPONSE';
setUpdateVc: 'VC_UPDATED';
updateMyVcs: 'VC_UPDATED';
};
'eventsCausingDelays': {};
'eventsCausingGuards': {
hasExistingReceivedVc: 'VC_RECEIVED';
};
'eventsCausingServices': {};
'matchesStates':
| 'init'
| 'init.myVcs'
| 'init.receivedVcs'
| 'ready'
| 'ready.myVcs'
| 'ready.myVcs.idle'
| 'ready.myVcs.refreshing'
| 'ready.receivedVcs'
| 'ready.receivedVcs.idle'
| 'ready.receivedVcs.refreshing'
| {
init?: 'myVcs' | 'receivedVcs';
ready?:
| 'myVcs'
| 'receivedVcs'
| {
myVcs?: 'idle' | 'refreshing';
receivedVcs?: 'idle' | 'refreshing';
};
};
'tags': never;
}

View File

@@ -25,7 +25,7 @@ import {
import getAllConfigurations, {
DownloadProps,
} from '../shared/commonprops/commonProps';
import i18n from '../i18n';
import { VcEvents } from './vc';
const model = createModel(
{
@@ -38,6 +38,7 @@ const model = createModel(
verifiableCredential: null as VerifiableCredential,
requestId: '',
isVerified: false,
isPinned: false,
lastVerifiedOn: null,
locked: false,
otp: '',
@@ -75,6 +76,7 @@ const model = createModel(
ADD_WALLET_BINDING_ID: () => ({}),
CANCEL: () => ({}),
CONFIRM: () => ({}),
PIN_CARD: () => ({}),
},
}
);
@@ -213,6 +215,19 @@ export const vcItemMachine =
ADD_WALLET_BINDING_ID: {
target: 'showBindingWarning',
},
PIN_CARD: {
target: 'pinCard',
actions: 'setPinCard',
},
},
},
pinCard: {
entry: 'storeContext',
on: {
STORE_RESPONSE: {
actions: 'sendVcUpdated',
target: 'idle',
},
},
},
editingTag: {
@@ -526,6 +541,21 @@ export const vcItemMachine =
event.data as WalletBindingResponse,
}),
setPinCard: assign((context) => {
return {
...context,
isPinned: !context.isPinned,
};
}),
sendVcUpdated: send(
(_context, event) =>
VcEvents.VC_UPDATED(VC_ITEM_STORE_KEY(event.response) as string),
{
to: (context) => context.serviceRefs.vc,
}
),
updateVc: send(
(context) => {
const { serviceRefs, ...vc } = context;
@@ -558,9 +588,7 @@ export const vcItemMachine =
const { serviceRefs, ...data } = context;
return StoreEvents.SET(VC_ITEM_STORE_KEY(context), data);
},
{
to: (context) => context.serviceRefs.store,
}
{ to: (context) => context.serviceRefs.store }
),
setTag: model.assign({
@@ -852,6 +880,7 @@ export const vcItemMachine =
tag: '',
requestId: context.requestId,
isVerified: false,
isPinned: context.isPinned,
lastVerifiedOn: null,
locked: context.locked,
walletBindingResponse: null,
@@ -1005,6 +1034,9 @@ export function selectIsOtpError(state: State) {
export function selectOtpError(state: State) {
return state.context.otpError;
}
export function selectIsPinned(state: State) {
return state.context.isPinned;
}
export function selectIsLockingVc(state: State) {
return state.matches('lockingVc');
@@ -1062,6 +1094,6 @@ export function isWalletBindingInProgress(state: State) {
: false;
}
export function isShowingBindingWarning(state: State) {
export function isShowBindingWarning(state: State) {
return state.matches('showBindingWarning');
}

View File

@@ -1,254 +1,109 @@
// This file was automatically generated. Edits will be overwritten
export interface Typegen0 {
'@@xstate/typegen': true;
'internalEvents': {
'': { type: '' };
'done.invoke.checkStatus': {
type: 'done.invoke.checkStatus';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.downloadCredential': {
type: 'done.invoke.downloadCredential';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.vc-item.addKeyPair:invocation[0]': {
type: 'done.invoke.vc-item.addKeyPair:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.vc-item.addingWalletBindingId:invocation[0]': {
type: 'done.invoke.vc-item.addingWalletBindingId:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]': {
type: 'done.invoke.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.vc-item.requestingBindingOtp:invocation[0]': {
type: 'done.invoke.vc-item.requestingBindingOtp:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.vc-item.requestingLock:invocation[0]': {
type: 'done.invoke.vc-item.requestingLock:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.vc-item.requestingOtp:invocation[0]': {
type: 'done.invoke.vc-item.requestingOtp:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.vc-item.requestingRevoke:invocation[0]': {
type: 'done.invoke.vc-item.requestingRevoke:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.vc-item.updatingPrivateKey:invocation[0]': {
type: 'done.invoke.vc-item.updatingPrivateKey:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'done.invoke.vc-item.verifyingCredential:invocation[0]': {
type: 'done.invoke.vc-item.verifyingCredential:invocation[0]';
data: unknown;
__tip: 'See the XState TS docs to learn how to strongly type this.';
};
'error.platform.checkStatus': {
type: 'error.platform.checkStatus';
data: unknown;
};
'error.platform.downloadCredential': {
type: 'error.platform.downloadCredential';
data: unknown;
};
'error.platform.vc-item.addKeyPair:invocation[0]': {
type: 'error.platform.vc-item.addKeyPair:invocation[0]';
data: unknown;
};
'error.platform.vc-item.addingWalletBindingId:invocation[0]': {
type: 'error.platform.vc-item.addingWalletBindingId:invocation[0]';
data: unknown;
};
'error.platform.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]': {
type: 'error.platform.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]';
data: unknown;
};
'error.platform.vc-item.requestingBindingOtp:invocation[0]': {
type: 'error.platform.vc-item.requestingBindingOtp:invocation[0]';
data: unknown;
};
'error.platform.vc-item.requestingLock:invocation[0]': {
type: 'error.platform.vc-item.requestingLock:invocation[0]';
data: unknown;
};
'error.platform.vc-item.requestingRevoke:invocation[0]': {
type: 'error.platform.vc-item.requestingRevoke:invocation[0]';
data: unknown;
};
'error.platform.vc-item.updatingPrivateKey:invocation[0]': {
type: 'error.platform.vc-item.updatingPrivateKey:invocation[0]';
data: unknown;
};
'error.platform.vc-item.verifyingCredential:invocation[0]': {
type: 'error.platform.vc-item.verifyingCredential:invocation[0]';
data: unknown;
};
'xstate.init': { type: 'xstate.init' };
};
'invokeSrcNameMap': {
addWalletBindnigId: 'done.invoke.vc-item.addingWalletBindingId:invocation[0]';
checkDownloadExpiryLimit: 'done.invoke.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]';
checkStatus: 'done.invoke.checkStatus';
downloadCredential: 'done.invoke.downloadCredential';
generateKeyPair: 'done.invoke.vc-item.addKeyPair:invocation[0]';
requestBindingOtp: 'done.invoke.vc-item.requestingBindingOtp:invocation[0]';
requestLock: 'done.invoke.vc-item.requestingLock:invocation[0]';
requestOtp: 'done.invoke.vc-item.requestingOtp:invocation[0]';
requestRevoke: 'done.invoke.vc-item.requestingRevoke:invocation[0]';
updatePrivateKey: 'done.invoke.vc-item.updatingPrivateKey:invocation[0]';
verifyCredential: 'done.invoke.vc-item.verifyingCredential:invocation[0]';
};
'missingImplementations': {
actions: never;
delays: never;
guards: never;
services: never;
};
'eventsCausingActions': {
clearOtp:
| ''
| 'CANCEL'
| 'DISMISS'
| 'REVOKE_VC'
| 'STORE_RESPONSE'
| 'done.invoke.vc-item.requestingBindingOtp:invocation[0]'
| 'done.invoke.vc-item.requestingOtp:invocation[0]'
| 'done.invoke.vc-item.updatingPrivateKey:invocation[0]'
| 'done.invoke.vc-item.verifyingCredential:invocation[0]'
| 'error.platform.vc-item.requestingLock:invocation[0]'
| 'error.platform.vc-item.requestingRevoke:invocation[0]'
| 'error.platform.vc-item.verifyingCredential:invocation[0]';
clearTransactionId:
| ''
| 'CANCEL'
| 'DISMISS'
| 'STORE_RESPONSE'
| 'done.invoke.vc-item.updatingPrivateKey:invocation[0]'
| 'done.invoke.vc-item.verifyingCredential:invocation[0]'
| 'error.platform.vc-item.verifyingCredential:invocation[0]';
incrementDownloadCounter: 'POLL';
logDownloaded: 'CREDENTIAL_DOWNLOADED';
logRevoked: 'STORE_RESPONSE';
markVcValid: 'done.invoke.vc-item.verifyingCredential:invocation[0]';
requestStoredContext: 'GET_VC_RESPONSE' | 'REFRESH';
requestVcContext: 'xstate.init';
revokeVID: 'done.invoke.vc-item.requestingRevoke:invocation[0]';
setCredential:
| 'CREDENTIAL_DOWNLOADED'
| 'GET_VC_RESPONSE'
| 'STORE_RESPONSE';
setDownloadInterval: 'done.invoke.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]';
setLock: 'done.invoke.vc-item.requestingLock:invocation[0]';
setMaxDownloadCount: 'done.invoke.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]';
setOtp: 'INPUT_OTP';
setOtpError:
| 'error.platform.vc-item.requestingLock:invocation[0]'
| 'error.platform.vc-item.requestingRevoke:invocation[0]';
setPrivateKey: 'done.invoke.vc-item.addKeyPair:invocation[0]';
setPublicKey: 'done.invoke.vc-item.addKeyPair:invocation[0]';
setRevoke: 'done.invoke.vc-item.requestingRevoke:invocation[0]';
setTag: 'SAVE_TAG';
setTransactionId:
| 'INPUT_OTP'
| 'REVOKE_VC'
| 'done.invoke.vc-item.requestingOtp:invocation[0]'
| 'error.platform.vc-item.requestingLock:invocation[0]'
| 'error.platform.vc-item.requestingRevoke:invocation[0]';
setWalletBindingError:
| 'error.platform.vc-item.addKeyPair:invocation[0]'
| 'error.platform.vc-item.addingWalletBindingId:invocation[0]'
| 'error.platform.vc-item.requestingBindingOtp:invocation[0]'
| 'error.platform.vc-item.updatingPrivateKey:invocation[0]';
setWalletBindingErrorEmpty:
| 'CANCEL'
| 'done.invoke.vc-item.updatingPrivateKey:invocation[0]';
setWalletBindingId: 'done.invoke.vc-item.addingWalletBindingId:invocation[0]';
storeContext:
| 'CREDENTIAL_DOWNLOADED'
| 'done.invoke.vc-item.updatingPrivateKey:invocation[0]'
| 'done.invoke.vc-item.verifyingCredential:invocation[0]';
storeLock: 'done.invoke.vc-item.requestingLock:invocation[0]';
storeTag: 'SAVE_TAG';
updatePrivateKey: 'done.invoke.vc-item.updatingPrivateKey:invocation[0]';
updateVc:
| 'CREDENTIAL_DOWNLOADED'
| 'STORE_RESPONSE'
| 'done.invoke.vc-item.updatingPrivateKey:invocation[0]'
| 'done.invoke.vc-item.verifyingCredential:invocation[0]';
};
'eventsCausingDelays': {};
'eventsCausingGuards': {
hasCredential: 'GET_VC_RESPONSE' | 'STORE_RESPONSE';
isDownloadAllowed: 'POLL';
isVcValid: '';
};
'eventsCausingServices': {
addWalletBindnigId: 'done.invoke.vc-item.addKeyPair:invocation[0]';
checkDownloadExpiryLimit: 'STORE_RESPONSE';
checkStatus:
| 'done.invoke.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]'
| 'error.platform.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]';
downloadCredential: 'DOWNLOAD_READY';
generateKeyPair: 'INPUT_OTP';
requestBindingOtp: 'CONFIRM';
requestLock: 'INPUT_OTP';
requestOtp: 'LOCK_VC';
requestRevoke: 'INPUT_OTP';
updatePrivateKey: 'done.invoke.vc-item.addingWalletBindingId:invocation[0]';
verifyCredential: '' | 'VERIFY';
};
'matchesStates':
| 'acceptingBindingOtp'
| 'acceptingOtpInput'
| 'acceptingRevokeInput'
| 'addKeyPair'
| 'addingWalletBindingId'
| 'checkingServerData'
| 'checkingServerData.checkingStatus'
| 'checkingServerData.downloadingCredential'
| 'checkingServerData.verifyingDownloadLimitExpiry'
| 'checkingStore'
| 'checkingVc'
| 'checkingVerificationStatus'
| 'editingTag'
| 'idle'
| 'invalid'
| 'invalid.backend'
| 'invalid.otp'
| 'lockingVc'
| 'loggingRevoke'
| 'requestingBindingOtp'
| 'requestingLock'
| 'requestingOtp'
| 'requestingRevoke'
| 'revokingVc'
| 'showBindingWarning'
| 'showingWalletBindingError'
| 'storingTag'
| 'updatingPrivateKey'
| 'verifyingCredential'
| {
checkingServerData?:
| 'checkingStatus'
| 'downloadingCredential'
| 'verifyingDownloadLimitExpiry';
invalid?: 'backend' | 'otp';
};
'tags': never;
}
// This file was automatically generated. Edits will be overwritten
export interface Typegen0 {
'@@xstate/typegen': true;
internalEvents: {
"": { type: "" };
"done.invoke.checkStatus": { type: "done.invoke.checkStatus"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.downloadCredential": { type: "done.invoke.downloadCredential"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.vc-item.addKeyPair:invocation[0]": { type: "done.invoke.vc-item.addKeyPair:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.vc-item.addingWalletBindingId:invocation[0]": { type: "done.invoke.vc-item.addingWalletBindingId:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]": { type: "done.invoke.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.vc-item.requestingBindingOtp:invocation[0]": { type: "done.invoke.vc-item.requestingBindingOtp:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.vc-item.requestingLock:invocation[0]": { type: "done.invoke.vc-item.requestingLock:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.vc-item.requestingOtp:invocation[0]": { type: "done.invoke.vc-item.requestingOtp:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.vc-item.requestingRevoke:invocation[0]": { type: "done.invoke.vc-item.requestingRevoke:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.vc-item.updatingPrivateKey:invocation[0]": { type: "done.invoke.vc-item.updatingPrivateKey:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"done.invoke.vc-item.verifyingCredential:invocation[0]": { type: "done.invoke.vc-item.verifyingCredential:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
"error.platform.checkStatus": { type: "error.platform.checkStatus"; data: unknown };
"error.platform.downloadCredential": { type: "error.platform.downloadCredential"; data: unknown };
"error.platform.vc-item.addKeyPair:invocation[0]": { type: "error.platform.vc-item.addKeyPair:invocation[0]"; data: unknown };
"error.platform.vc-item.addingWalletBindingId:invocation[0]": { type: "error.platform.vc-item.addingWalletBindingId:invocation[0]"; data: unknown };
"error.platform.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]": { type: "error.platform.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]"; data: unknown };
"error.platform.vc-item.requestingBindingOtp:invocation[0]": { type: "error.platform.vc-item.requestingBindingOtp:invocation[0]"; data: unknown };
"error.platform.vc-item.requestingLock:invocation[0]": { type: "error.platform.vc-item.requestingLock:invocation[0]"; data: unknown };
"error.platform.vc-item.requestingRevoke:invocation[0]": { type: "error.platform.vc-item.requestingRevoke:invocation[0]"; data: unknown };
"error.platform.vc-item.updatingPrivateKey:invocation[0]": { type: "error.platform.vc-item.updatingPrivateKey:invocation[0]"; data: unknown };
"error.platform.vc-item.verifyingCredential:invocation[0]": { type: "error.platform.vc-item.verifyingCredential:invocation[0]"; data: unknown };
"xstate.init": { type: "xstate.init" };
};
invokeSrcNameMap: {
"addWalletBindnigId": "done.invoke.vc-item.addingWalletBindingId:invocation[0]";
"checkDownloadExpiryLimit": "done.invoke.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]";
"checkStatus": "done.invoke.checkStatus";
"downloadCredential": "done.invoke.downloadCredential";
"generateKeyPair": "done.invoke.vc-item.addKeyPair:invocation[0]";
"requestBindingOtp": "done.invoke.vc-item.requestingBindingOtp:invocation[0]";
"requestLock": "done.invoke.vc-item.requestingLock:invocation[0]";
"requestOtp": "done.invoke.vc-item.requestingOtp:invocation[0]";
"requestRevoke": "done.invoke.vc-item.requestingRevoke:invocation[0]";
"updatePrivateKey": "done.invoke.vc-item.updatingPrivateKey:invocation[0]";
"verifyCredential": "done.invoke.vc-item.verifyingCredential:invocation[0]";
};
missingImplementations: {
actions: never;
delays: never;
guards: never;
services: never;
};
eventsCausingActions: {
"clearOtp": "" | "CANCEL" | "DISMISS" | "REVOKE_VC" | "STORE_RESPONSE" | "done.invoke.vc-item.requestingBindingOtp:invocation[0]" | "done.invoke.vc-item.requestingOtp:invocation[0]" | "done.invoke.vc-item.updatingPrivateKey:invocation[0]" | "done.invoke.vc-item.verifyingCredential:invocation[0]" | "error.platform.vc-item.requestingLock:invocation[0]" | "error.platform.vc-item.requestingRevoke:invocation[0]" | "error.platform.vc-item.verifyingCredential:invocation[0]";
"clearTransactionId": "" | "CANCEL" | "DISMISS" | "STORE_RESPONSE" | "done.invoke.vc-item.updatingPrivateKey:invocation[0]" | "done.invoke.vc-item.verifyingCredential:invocation[0]" | "error.platform.vc-item.verifyingCredential:invocation[0]";
"incrementDownloadCounter": "POLL";
"logDownloaded": "CREDENTIAL_DOWNLOADED";
"logRevoked": "STORE_RESPONSE";
"logWalletBindingFailure": "error.platform.vc-item.addKeyPair:invocation[0]" | "error.platform.vc-item.addingWalletBindingId:invocation[0]" | "error.platform.vc-item.requestingBindingOtp:invocation[0]" | "error.platform.vc-item.updatingPrivateKey:invocation[0]";
"logWalletBindingSuccess": "done.invoke.vc-item.updatingPrivateKey:invocation[0]";
"markVcValid": "done.invoke.vc-item.verifyingCredential:invocation[0]";
"requestStoredContext": "GET_VC_RESPONSE" | "REFRESH";
"requestVcContext": "xstate.init";
"revokeVID": "done.invoke.vc-item.requestingRevoke:invocation[0]";
"sendVcUpdated": "STORE_RESPONSE";
"setCredential": "CREDENTIAL_DOWNLOADED" | "GET_VC_RESPONSE" | "STORE_RESPONSE";
"setDownloadInterval": "done.invoke.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]";
"setLock": "done.invoke.vc-item.requestingLock:invocation[0]";
"setMaxDownloadCount": "done.invoke.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]";
"setOtp": "INPUT_OTP";
"setOtpError": "error.platform.vc-item.requestingLock:invocation[0]" | "error.platform.vc-item.requestingRevoke:invocation[0]";
"setPinCard": "PIN_CARD";
"setPrivateKey": "done.invoke.vc-item.addKeyPair:invocation[0]";
"setPublicKey": "done.invoke.vc-item.addKeyPair:invocation[0]";
"setRevoke": "done.invoke.vc-item.requestingRevoke:invocation[0]";
"setTag": "SAVE_TAG";
"setTransactionId": "INPUT_OTP" | "REVOKE_VC" | "done.invoke.vc-item.requestingOtp:invocation[0]" | "error.platform.vc-item.requestingLock:invocation[0]" | "error.platform.vc-item.requestingRevoke:invocation[0]";
"setWalletBindingError": "error.platform.vc-item.addKeyPair:invocation[0]" | "error.platform.vc-item.addingWalletBindingId:invocation[0]" | "error.platform.vc-item.requestingBindingOtp:invocation[0]" | "error.platform.vc-item.updatingPrivateKey:invocation[0]";
"setWalletBindingErrorEmpty": "CANCEL" | "done.invoke.vc-item.updatingPrivateKey:invocation[0]";
"setWalletBindingId": "done.invoke.vc-item.addingWalletBindingId:invocation[0]";
"storeContext": "CREDENTIAL_DOWNLOADED" | "PIN_CARD" | "done.invoke.vc-item.updatingPrivateKey:invocation[0]" | "done.invoke.vc-item.verifyingCredential:invocation[0]";
"storeLock": "done.invoke.vc-item.requestingLock:invocation[0]";
"storeTag": "SAVE_TAG";
"updatePrivateKey": "done.invoke.vc-item.updatingPrivateKey:invocation[0]";
"updateVc": "CREDENTIAL_DOWNLOADED" | "STORE_RESPONSE" | "done.invoke.vc-item.updatingPrivateKey:invocation[0]" | "done.invoke.vc-item.verifyingCredential:invocation[0]";
};
eventsCausingDelays: {
};
eventsCausingGuards: {
"hasCredential": "GET_VC_RESPONSE" | "STORE_RESPONSE";
"isDownloadAllowed": "POLL";
"isVcValid": "";
};
eventsCausingServices: {
"addWalletBindnigId": "done.invoke.vc-item.addKeyPair:invocation[0]";
"checkDownloadExpiryLimit": "STORE_RESPONSE";
"checkStatus": "done.invoke.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]" | "error.platform.vc-item.checkingServerData.verifyingDownloadLimitExpiry:invocation[0]";
"downloadCredential": "DOWNLOAD_READY";
"generateKeyPair": "INPUT_OTP";
"requestBindingOtp": "CONFIRM";
"requestLock": "INPUT_OTP";
"requestOtp": "LOCK_VC";
"requestRevoke": "INPUT_OTP";
"updatePrivateKey": "done.invoke.vc-item.addingWalletBindingId:invocation[0]";
"verifyCredential": "" | "VERIFY";
};
matchesStates: "acceptingBindingOtp" | "acceptingOtpInput" | "acceptingRevokeInput" | "addKeyPair" | "addingWalletBindingId" | "checkingServerData" | "checkingServerData.checkingStatus" | "checkingServerData.downloadingCredential" | "checkingServerData.verifyingDownloadLimitExpiry" | "checkingStore" | "checkingVc" | "checkingVerificationStatus" | "editingTag" | "idle" | "invalid" | "invalid.backend" | "invalid.otp" | "lockingVc" | "loggingRevoke" | "pinCard" | "requestingBindingOtp" | "requestingLock" | "requestingOtp" | "requestingRevoke" | "revokingVc" | "showBindingWarning" | "showingWalletBindingError" | "storingTag" | "updatingPrivateKey" | "verifyingCredential" | { "checkingServerData"?: "checkingStatus" | "downloadingCredential" | "verifyingDownloadLimitExpiry";
"invalid"?: "backend" | "otp"; };
tags: never;
}

View File

@@ -1,345 +0,0 @@
import { TextInput } from 'react-native';
import { assign, ErrorPlatformEvent, StateFrom, send, EventFrom } from 'xstate';
import { log } from 'xstate/lib/actions';
import i18n from '../i18n';
import { AppServices } from '../shared/GlobalContext';
import { ActivityLogEvents } from './activityLog';
import { StoreEvents } from './store';
import { createModel } from 'xstate/lib/model';
import { request } from '../shared/request';
import { VcIdType } from '../types/vc';
import { MY_VCS_STORE_KEY, VC_ITEM_STORE_KEY } from '../shared/constants';
import {
generateKeys,
WalletBindingResponse,
} from '../shared/cryptoutil/cryptoUtil';
import { KeyPair } from 'react-native-rsa-native';
import {
getBindingCertificateConstant,
savePrivateKey,
} from '../shared/keystore/SecureKeystore';
const model = createModel(
{
serviceRefs: {} as AppServices,
idType: 'VID' as VcIdType,
id: '',
idError: '',
otp: '',
otpError: '',
transactionId: '',
requestId: '',
VIDs: [] as string[],
bindingTransactionId: '',
walletBindingResponse: null as WalletBindingResponse,
walletBindingError: '',
publicKey: '',
privateKey: '',
},
{
events: {
INPUT_OTP: (otp: string) => ({ otp }),
VALIDATE_INPUT: () => ({}),
READY: (idInputRef: TextInput) => ({ idInputRef }),
DISMISS: () => ({}),
SELECT_ID_TYPE: (idType: VcIdType) => ({ idType }),
REVOKE_VCS: (vcKeys: string[]) => ({ vcKeys }),
STORE_RESPONSE: (response: string[]) => ({ response }),
ERROR: (data: Error) => ({ data }),
SUCCESS: () => ({}),
CONFIRM: () => ({}),
CANCEL: () => ({}),
},
}
);
export const walletBindingMachine =
/** @xstate-layout N4IgpgJg5mDOIC5QCUwDcD2BrMA1AlhLAHSEA2YAxMgKK4DyA0jQPq4DCAyoqAA4ax8AF3wYAdjxAAPRACYAHAFZiARhUB2WQDZFABgCcO-boDMWgDQgAnohWyALLOK6X9k-J3zZirfYC+fpaomDgERKRiaACGZISUAJIAcgAKAKoAKiz06cmS-IIi4pIyCOq6TuomjvYqWlpe3iaWNgiKik4u5fK69vr2uipmAUHo2HiEJPiRMXEAIvGcALIL3Egg+cKiEmslZRVVDrX1so3Nclr6zi61pvLyKoY9wyDBY2EkUQDGn2C8ImJQXDxWaTCAUah0JisDirPgCTZFHaIEyydTEe6yEyaQzqXrtM4IeQmXRXcrqDz9dQPEzPV6hCbEL4-P5TQHAkgAJzAAEcAK5wf5QehCXiUCDiMAREKSunjcJM36CoEg4hcvkC1nC3gIKaYT5RQpiADaugAunl4Ybioh8dZbHtiHoevpFESlIp9DTAi9RvT5d9FazlZyefzYIKtZQwByORgOcReGQDQAzOMAW2IsvejIDLIBwdVoY1AK1OsiGH1hpN5rWGytSIQJix6LsWNkOLxsgJKLRnVMlXJdxq-m9WYZCrzbJBlHmSxWFoKW2tjdRLcx2K0uJdXbtCGMpN03Qe-X03VktN9co+uYjIviYl4vKECRSGSyOQXCO2oBKKkPaNdRQqXsRRBi0EwXQJep5EdFxURdTczB0C9pWzCdb14e9H2fWdlk4WF1ktJcGwUZQ1E0HQDCMUwLF3NQ+lJVFN3kXFvBQt4GTVMNBVlMUJSlMZM0vbMuOLKBZTLPUDS2atP3rH9bH-R0lGA0CzAgxQCRqS4nQ0F0-10SoRxGVDOKLcNWV46NY3jRMU3TITTPCUSLIBCTdQraTxFk2siMRBTSh3FpBhdWCeiAqkiSA4yfSckMQiDT4ZwWPCCLrYiAq8AlBgeYg6jKLF1DaB5ygCb0xAwCA4EkMdwnIMA5Iy6RbEMPKNGJSo2nuD17Gyh5lDqC59HUTwVBqL0TI4urpliCBiAwEVGv85rWipZx5GGxRiV8GjNN3BQALg8bMS0citHYv1JhmwhiAAIy+HAxAgJbvxWto0T6doTlkFwtBOFRuzaMK1G6TdqWGi6rylGZnt8xdlpKLaTGIT7vp+3Q-tkAH9pUZRdNOux9KxGLauvZklXZUgwQauGv2XH7LhMOwai+zcfvkXrd3sHQwsPal7C8WpFEhtCbyDSmXIwl7l00btud5twLj-Op5BF8cxfzdlpYbAXspYhW9iG3w1f9cnNTvB8n21gKBbROpHEAhn20UTngp+lRiCx7nN1uAxMRNkN1Vc8TL2tlaiTRQZtBMCKBe+gkXZJJ1tB6ECBf0FQA8LBL80+MPf3bGC7lRDHT1TptuzuMLDhRMwzoD-O5Fd2wYOuVF-raXx7HUMq-CAA */
model.createMachine(
{
predictableActionArguments: true,
preserveActionOrder: true,
tsTypes: {} as import('./WalletBinding.typegen').Typegen0,
schema: {
context: model.initialContext,
events: {} as EventFrom<typeof model>,
},
id: 'walletBinding',
initial: 'showBindingWarning',
states: {
showBindingWarning: {
on: {
CONFIRM: {
target: 'requestingBindingOtp',
},
CANCEL: {
target: 'showBindingWarning',
},
},
},
requestingBindingOtp: {
invoke: {
src: 'requestBindingOtp',
onDone: [
{
target: 'acceptingBindingOtp',
},
],
onError: [
{
actions: 'setWalletBindingError',
target: 'showingWalletBindingError',
},
],
},
},
showingWalletBindingError: {
on: {
CANCEL: {
target: 'showBindingWarning',
actions: 'setWalletBindingErrorEmpty',
},
},
},
acceptingBindingOtp: {
entry: ['clearOtp'],
on: {
INPUT_OTP: {
target: 'addKeyPair',
actions: ['setOtp'],
},
DISMISS: {
target: '',
actions: ['clearOtp', 'clearTransactionId'],
},
},
},
addKeyPair: {
invoke: {
src: 'generateKeyPair',
onDone: {
target: 'addingWalletBindingId',
actions: ['setPublicKey', 'setPrivateKey'],
},
onError: [
{
actions: 'setWalletBindingError',
target: 'showingWalletBindingError',
},
],
},
},
addingWalletBindingId: {
invoke: {
src: 'addWalletBindnigId',
onDone: [
{
target: 'updatingPrivateKey',
actions: ['setWalletBindingId'],
},
],
onError: [
{
actions: 'setWalletBindingError',
target: 'showingWalletBindingError',
},
],
},
},
updatingPrivateKey: {
invoke: {
src: 'updatePrivateKey',
onDone: {
target: '',
actions: [
'storeContext',
'updatePrivateKey',
'updateVc',
'setWalletBindingErrorEmpty',
],
},
onError: {
actions: 'setWalletBindingError',
target: 'showingWalletBindingError',
},
},
},
},
},
{
actions: {
setWalletBindingError: assign({
walletBindingError: (context, event) => (event.data as Error).message,
}),
setWalletBindingErrorEmpty: assign({
walletBindingError: () => '',
}),
setPublicKey: assign({
publicKey: (context, event) => (event.data as KeyPair).public,
}),
setPrivateKey: assign({
privateKey: (context, event) => (event.data as KeyPair).private,
}),
updatePrivateKey: assign({
privateKey: () => '',
}),
setWalletBindingId: assign({
walletBindingResponse: (context, event) =>
event.data as WalletBindingResponse,
}),
setOtp: model.assign({
otp: (_context, event) => event.otp,
}),
clearOtp: assign({ otp: '' }),
storeContext: send(
(context) => {
const { serviceRefs, ...data } = context;
return StoreEvents.SET(VC_ITEM_STORE_KEY(context), data);
},
{
to: (context) => context.serviceRefs.store,
}
),
updateVc: send(
(context) => {
const { serviceRefs, ...vc } = context;
return { type: 'VC_DOWNLOADED', vc };
},
{
to: (context) => context.serviceRefs.vc,
}
),
},
services: {
addWalletBindnigId: async (context) => {
const response = await request(
'POST',
'/residentmobileapp/wallet-binding',
{
requestTime: String(new Date().toISOString()),
request: {
authFactorType: 'WLA',
format: 'jwt',
individualId: context.id,
transactionId: context.bindingTransactionId,
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;
},
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 '';
},
generateKeyPair: async (context) => {
let keyPair: KeyPair = await generateKeys();
return keyPair;
},
requestBindingOtp: async (context) => {
console.log('requesting otp');
const response = await request(
'POST',
'/residentmobileapp/binding-otp',
{
requestTime: String(new Date().toISOString()),
request: {
individualId: context.id,
otpChannels: ['EMAIL', 'PHONE'],
},
}
);
if (response.response == null) {
throw new Error('Could not process request');
}
},
},
guards: {},
}
);
export function createWalletBindingMachine(serviceRefs: AppServices) {
return walletBindingMachine.withContext({
...walletBindingMachine.context,
serviceRefs,
});
}
type State = StateFrom<typeof walletBindingMachine>;
export const WalletBindingEvents = model.events;
export function selectIdType(state: State) {
return state.context.idType;
}
export function selectIdError(state: State) {
return state.context.idError;
}
export function selectOtpError(state: State) {
return state.context.otpError;
}
export function selectWalletBindingError(state: State) {
return state.context.walletBindingError;
}
export function selectIsBindingWarning(state: State) {
return state.matches('showBindingWarning');
}
export function selectIsAcceptingOtpInput(state: State) {
return state.matches('acceptingBindingOtp');
}
export function walletBindingInProgress(state: State) {
return state.matches('requestingBindingOtp') ||
state.matches('addingWalletBindingId') ||
state.matches('addKeyPair') ||
state.matches('updatingPrivateKey')
? true
: false;
}
export function selectShowWalletBindingError(state: State) {
return state.matches('showingWalletBindingError');
}

1124
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -18,6 +18,7 @@
"@digitalbazaar/rsa-signature-2018": "digitalbazaar/rsa-signature-2018#initial",
"@digitalbazaar/rsa-verification-key-2018": "digitalbazaar/rsa-verification-key-2018#initial",
"@digitalcredentials/vc": "^1.1.2",
"@expo-google-fonts/inter": "^0.2.3",
"@expo-google-fonts/poppins": "^0.2.0",
"@expo/metro-config": "^0.3.12",
"@idpass/smartshare-react-native": "0.2.3-beta.2",

View File

@@ -51,7 +51,7 @@ export const IntroSlidersScreen: React.FC<RootRouteProps> = (props) => {
return (
<LinearGradient colors={Theme.Colors.gradientBtn}>
<Centered>
<Row align="space-between" crossAlign="center">
<Row crossAlign="center">
<Column
style={{
flex: 3,
@@ -81,7 +81,6 @@ export const IntroSlidersScreen: React.FC<RootRouteProps> = (props) => {
<Column
style={Theme.OnboardingOverlayStyles.bottomContainer}
crossAlign="center"
align="space-between"
backgroundColor={Theme.Colors.whiteText}
width={Dimensions.get('screen').width}>
<Text weight="semibold">{item.title}</Text>
@@ -98,7 +97,7 @@ export const IntroSlidersScreen: React.FC<RootRouteProps> = (props) => {
<View>
<LinearGradient
colors={Theme.Colors.gradientBtn}
style={{ borderRadius: 10, height: 50 }}>
style={{ borderRadius: 10, height: 50, marginTop: -10 }}>
<Text
weight="semibold"
align="center"
@@ -115,16 +114,14 @@ export const IntroSlidersScreen: React.FC<RootRouteProps> = (props) => {
<View>
<LinearGradient
colors={Theme.Colors.gradientBtn}
style={{ borderRadius: 10, height: 50 }}>
<TouchableOpacity onPress={controller.NEXT}>
<Text
weight="semibold"
align="center"
color="#FFFFFF"
margin="10 0 0 0">
{t('stepThreeButton')}
</Text>
</TouchableOpacity>
style={{ borderRadius: 10, height: 50, marginTop: -10 }}>
<Text
weight="semibold"
align="center"
color="#FFFFFF"
margin="10 0 0 0">
{t('stepThreeButton')}
</Text>
</LinearGradient>
</View>
);
@@ -141,6 +138,7 @@ export const IntroSlidersScreen: React.FC<RootRouteProps> = (props) => {
activeDotStyle={{ backgroundColor: Theme.Colors.Icon }}
dotStyle={{ backgroundColor: Theme.Colors.dotColor }}
renderItem={renderItem}
onDone={() => controller.NEXT()}
/>
</View>
);

View File

@@ -18,6 +18,7 @@ export const AddVcModal: React.FC<AddVcModalProps> = (props) => {
}
onDismiss={controller.DISMISS}
onPress={props.onPress}
headerTitle={t('inputIdHeader')}
/>
<OtpVerificationModal

View File

@@ -23,6 +23,7 @@ const model = createModel(
otpError: '',
transactionId: '',
requestId: '',
isPinned: false,
},
{
events: {

View File

@@ -1,32 +1,31 @@
import React, { useState } from 'react';
import {
Dimensions,
I18nManager,
RefreshControl,
SafeAreaView,
View,
} from 'react-native';
import { Divider, Icon, ListItem, Overlay } from 'react-native-elements';
import { Button, Column, Centered, Row, Text } from '../../../components/ui';
import { VidItem } from '../../../components/VidItem';
import React from 'react';
import { Icon, ListItem } from 'react-native-elements';
import { Column, Row, Text } from '../../../components/ui';
import { Theme } from '../../../components/ui/styleUtils';
import { ToastItem } from '../../../components/ui/ToastItem';
import { OIDcAuthenticationOverlay } from '../../../components/OIDcAuthModal';
import { useTranslation } from 'react-i18next';
import { useRevoke } from '../../Profile/RevokeController';
import { BindingVcWarningOverlay } from './BindingVcWarningOverlay';
import { useViewVcModal } from '../ViewVcModalController';
import { OtpVerification } from './OtpVerification';
import { useWalletBinding } from './WalletBindingController';
import { MessageOverlay } from '../../../components/MessageOverlay';
import { useKebabPopUp } from '../../../components/KebabPopUpController';
import { Dimensions } from 'react-native';
export const WalletBinding: React.FC<WalletBindingProps> = (props) => {
const controller = useWalletBinding();
const controller = useKebabPopUp(props);
const WalletVerified: React.FC = () => {
return (
<Icon
name="verified-user"
color={Theme.Colors.VerifiedIcon}
size={28}
containerStyle={{ marginStart: 4, bottom: 1 }}
/>
);
};
const { t } = useTranslation('ProfileScreen');
return (
<ListItem bottomDivider onPress={() => controller.setBindingWarning(true)}>
return controller.emptyWalletBindingId ? (
<ListItem bottomDivider onPress={controller.ADD_WALLET_BINDING_ID}>
{props.Icon && (
<Icon
name={props.Icon}
@@ -56,7 +55,7 @@ export const WalletBinding: React.FC<WalletBindingProps> = (props) => {
<BindingVcWarningOverlay
isVisible={controller.isBindingWarning}
onConfirm={controller.CONFIRM}
onCancel={controller.DISMISS}
onCancel={controller.CANCEL}
/>
<OtpVerification
@@ -71,11 +70,28 @@ export const WalletBinding: React.FC<WalletBindingProps> = (props) => {
onCancel={controller.CANCEL}
/>
<MessageOverlay
isVisible={controller.isWalletBindingInProgress}
isVisible={controller.WalletBindingInProgress}
title={t('inProgress')}
progress
/>
</ListItem>
) : (
<ListItem bottomDivider>
<Row
width={Dimensions.get('screen').width * 0.8}
align="space-between"
crossAlign="center">
<Row crossAlign="center" style={{ flex: 1 }}>
<WalletVerified />
<Text
color={Theme.Colors.Details}
weight="semibold"
size="smaller"
margin="10 10 10 10"
children={t('profileAuthenticated')}></Text>
</Row>
</Row>
</ListItem>
);
};
@@ -83,4 +99,5 @@ interface WalletBindingProps {
label: string;
Content?: string;
Icon?: string;
vcKey: string;
}

View File

@@ -1,95 +0,0 @@
import { useSelector } from '@xstate/react';
import { useContext, useEffect, useState } from 'react';
import NetInfo from '@react-native-community/netinfo';
import { GlobalContext } from '../../../shared/GlobalContext';
import { selectMyVcs, VcEvents } from '../../../machines/vc';
import { vcItemMachine } from '../../../machines/vcItem';
import { useTranslation } from 'react-i18next';
import { ActorRefFrom } from 'xstate';
import {
selectIsAcceptingOtpInput,
walletBindingInProgress,
selectOtpError,
WalletBindingEvents,
selectShowWalletBindingError,
selectWalletBindingError,
} from '../../../machines/walletBinding';
export function useWalletBinding() {
const { t } = useTranslation('ProfileScreen');
const { appService } = useContext(GlobalContext);
const vcService = appService.children.get('vc');
const walletBindingService = appService.children.get('walletBinding');
const vcKeys = useSelector(vcService, selectMyVcs);
// const isBindingWarning = useSelector(
// walletBindingService,
// selectIsBindingWarning
// );
const isAcceptingOtpInput = useSelector(
walletBindingService,
selectIsAcceptingOtpInput
);
const isWalletBindingInProgress = useSelector(
walletBindingService,
walletBindingInProgress
);
const isWalletBindingError = useSelector(
walletBindingService,
selectShowWalletBindingError
);
const walletBindingError = useSelector(
walletBindingService,
selectWalletBindingError
);
const otpError = useSelector(walletBindingService, selectOtpError);
const [isRevoking, setRevoking] = useState(false);
const [isAuthenticating, setAuthenticating] = useState(false);
const [isViewing, setIsViewing] = useState(false);
const [toastVisible, setToastVisible] = useState(false);
const [message, setMessage] = useState('');
const [selectedIndex, setSelectedIndex] = useState<number>(null);
const [selectedVidKeys, setSelectedVidKeys] = useState<string[]>([]);
const [isBindingWarning, setBindingWarning] = useState(false);
const [isAcceptingBindingOtp, setAcceptingBindingOtp] = useState(false);
const selectVcItem = (index: number, vcKey: string) => {
return () => {
setSelectedIndex(index);
};
};
return {
isBindingWarning,
otpError,
isAcceptingOtpInput,
isWalletBindingInProgress,
isWalletBindingError,
walletBindingError,
DISMISS: () => {
walletBindingService.send(WalletBindingEvents.DISMISS());
},
INPUT_OTP: (otp: string) =>
walletBindingService.send(WalletBindingEvents.INPUT_OTP(otp)),
REFRESH: () => vcService.send(VcEvents.REFRESH_MY_VCS()),
CONFIRM: () => {
walletBindingService.send(WalletBindingEvents.CONFIRM());
},
CANCEL: () => {
walletBindingService.send(WalletBindingEvents.CANCEL());
},
setBindingWarning,
setAuthenticating,
selectVcItem,
setIsViewing,
setRevoking,
};
}
export interface RevokeProps {
service: ActorRefFrom<typeof vcItemMachine>;
}

View File

@@ -8,7 +8,6 @@ import { HomeScreenTabProps } from './HomeScreen';
import { AddVcModal } from './MyVcs/AddVcModal';
import { GetVcModal } from './MyVcs/GetVcModal';
import { DownloadingVcModal } from './MyVcs/DownloadingVcModal';
import { OnboardingOverlay } from './OnboardingOverlay';
import { useTranslation } from 'react-i18next';
import { VcItem } from '../../components/VcItem';
import { GET_INDIVIDUAL_ID } from '../../shared/constants';
@@ -41,14 +40,32 @@ export const MyVcsTab: React.FC<HomeScreenTabProps> = (props) => {
onRefresh={controller.REFRESH}
/>
}>
{controller.vcKeys.map((vcKey, index) => (
<VcItem
key={`${vcKey}-${index}`}
vcKey={vcKey}
margin="0 2 8 2"
onPress={controller.VIEW_VC}
/>
))}
{controller.vcKeys.map((vcKey, index) => {
if (vcKey.split(':')[4] === 'true') {
return (
<VcItem
key={`${vcKey}-${index}`}
vcKey={vcKey}
margin="0 2 8 2"
onPress={controller.VIEW_VC}
iconName="pushpin"
iconType="antdesign"
/>
);
}
})}
{controller.vcKeys.map((vcKey, index) => {
if (vcKey.split(':')[4] === 'false') {
return (
<VcItem
key={`${vcKey}-${index}`}
vcKey={vcKey}
margin="0 2 8 2"
onPress={controller.VIEW_VC}
/>
);
}
})}
</Column>
<Column elevation={2} margin="10 2 0 2">
<Button
@@ -108,12 +125,6 @@ export const MyVcsTab: React.FC<HomeScreenTabProps> = (props) => {
onShow={clearIndividualId}
/>
)}
<OnboardingOverlay
isVisible={controller.isOnboarding}
onDone={controller.ONBOARDING_DONE}
onAddVc={controller.ADD_VC}
/>
</React.Fragment>
);
};

View File

@@ -18,9 +18,9 @@ export interface Typegen0 {
'invokeSrcNameMap': {};
'missingImplementations': {
actions: never;
services: never;
guards: never;
delays: never;
guards: never;
services: 'AddVcModal' | 'GetVcModal';
};
'eventsCausingActions': {
completeOnboarding: 'ADD_VC' | 'ONBOARDING_DONE';
@@ -29,11 +29,14 @@ export interface Typegen0 {
storeVcItem: 'done.invoke.AddVcModal';
viewVcFromParent: 'VIEW_VC';
};
'eventsCausingServices': {};
'eventsCausingDelays': {};
'eventsCausingGuards': {
isOnboardingDone: 'STORE_RESPONSE';
};
'eventsCausingDelays': {};
'eventsCausingServices': {
AddVcModal: 'ADD_VC' | 'done.invoke.GetVcModal';
GetVcModal: 'GET_VC';
};
'matchesStates':
| 'addingVc'
| 'addingVc.addVcSuccessful'

View File

@@ -22,7 +22,7 @@ import {
selectEmptyWalletBindingId,
isWalletBindingInProgress,
selectShowWalletBindingError,
isShowingBindingWarning,
isShowBindingWarning,
} from '../../machines/vcItem';
import { selectPasscode } from '../../machines/auth';
import { biometricsMachine, selectIsSuccess } from '../../machines/biometrics';
@@ -142,7 +142,7 @@ export function useViewVcModal({
isWalletBindingInProgress
),
isBindingError: useSelector(vcItemActor, selectShowWalletBindingError),
isBindingWarning: useSelector(vcItemActor, isShowingBindingWarning),
isBindingWarning: useSelector(vcItemActor, isShowBindingWarning),
CONFIRM_REVOKE_VC: () => {
setRevoking(true);

View File

@@ -11,8 +11,6 @@ import { settingsMachine } from '../machines/settings';
import { storeMachine } from '../machines/store';
import { vcMachine } from '../machines/vc';
import { revokeVidsMachine } from '../machines/revoke';
import { qrLoginMachine } from '../machines/QrLoginMachine';
export const GlobalContext = createContext({} as GlobalServices);
export interface GlobalServices {

View File

@@ -13,7 +13,7 @@ export const RECEIVED_VCS_STORE_KEY = 'receivedVCs';
export const MY_LOGIN_STORE_KEY = 'myLogins';
export const VC_ITEM_STORE_KEY = (vc: Partial<VC>) =>
`vc:${vc.idType}:${vc.id}:${vc.requestId}`;
`vc:${vc.idType}:${vc.id}:${vc.requestId}:${vc.isPinned}`;
export let individualId = '';

View File

@@ -1,15 +1,15 @@
import {
Poppins_400Regular,
Poppins_600SemiBold,
Poppins_700Bold,
useFonts,
} from '@expo-google-fonts/poppins';
Inter_400Regular,
Inter_600SemiBold,
Inter_700Bold,
} from '@expo-google-fonts/inter';
export function useFont() {
const [hasFontsLoaded] = useFonts({
Poppins_400Regular,
Poppins_600SemiBold,
Poppins_700Bold,
Inter_400Regular,
Inter_600SemiBold,
Inter_700Bold,
});
return hasFontsLoaded;

View File

@@ -15,6 +15,7 @@ export interface VC {
reason?: VCSharingReason[];
shouldVerifyPresence?: boolean;
walletBindingResponse?: WalletBindingResponse;
isPinned?: boolean;
}
export interface VCSharingReason {