mirror of
https://github.com/mosip/inji-wallet.git
synced 2026-01-09 13:38:01 -05:00
Inji-344: Refactoring VC Key (#798)
* feat(inji-344): Use VC Key class instead of separate functions for managing vc key * feat(inji-344): Use properties from VcKey Class instead of reading from vckey string * feat(inji-344): Rename vcKey to vcMetadata * feat(inji-344): Use vc's unique id or vckey instead of joined string of vc metadata * feat(inji-344): Use vc key instead of unique id to avoid confusion. Fix issues reg parsing vc metadata * feat(inji-344):fix redownloading issue Co-authored-by: Tilak <tilakpuli15@gmail.com> * feat(inji-344): Remove vc getting stored on update of pin status * feat(inji-344): update other vc's pin status to false when any vc is pinned * feat(inji-344): remove hash ID for UIN * feat(inji-344): revert google services json * feat(inji-344): remove mmkv logs added for debugging * feat(inji-344): fix received vcs not getting displayed on reopen of app * feat(inji-344): fix id not shown in revoke component --------- Co-authored-by: Sri Kanth Kola <srikanthsri7447@gmail.com>
This commit is contained in:
4
App.tsx
4
App.tsx
@@ -64,7 +64,7 @@ const AppLoadingWrapper: React.FC = () => {
|
||||
const isReadError = useSelector(appService, selectIsReadError);
|
||||
const isKeyInvalidateError = useSelector(
|
||||
appService,
|
||||
selectIsKeyInvalidateError
|
||||
selectIsKeyInvalidateError,
|
||||
);
|
||||
const controller = useApp();
|
||||
const {t} = useTranslation('WelcomeScreen');
|
||||
@@ -102,7 +102,7 @@ const AppInitialization: React.FC = () => {
|
||||
if (isCustomSecureKeystore()) {
|
||||
SecureKeystore.updatePopup(
|
||||
t('biometricPopup.title'),
|
||||
t('biometricPopup.description')
|
||||
t('biometricPopup.description'),
|
||||
);
|
||||
}
|
||||
}, [i18n.language]);
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Text, Column, Row, Button } from './ui';
|
||||
import {Theme} from './ui/styleUtils';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
|
||||
export const EditableListItem: React.FC<EditableListItemProps> = (props) => {
|
||||
export const EditableListItem: React.FC<EditableListItemProps> = props => {
|
||||
const {t} = useTranslation('common');
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [items, setItems] = useState(props.items);
|
||||
@@ -18,7 +18,7 @@ export const EditableListItem: React.FC<EditableListItemProps> = (props) => {
|
||||
}, [props.response]);
|
||||
|
||||
function updateItems(label: string, value: string) {
|
||||
const updatedItems = items.map((item) => {
|
||||
const updatedItems = items.map(item => {
|
||||
if (item.label === label) {
|
||||
return {...item, value: value};
|
||||
}
|
||||
@@ -61,7 +61,7 @@ export const EditableListItem: React.FC<EditableListItemProps> = (props) => {
|
||||
<Input
|
||||
autoFocus
|
||||
value={items[index].value}
|
||||
onChangeText={(value) => updateItems(item.label, value)}
|
||||
onChangeText={value => updateItems(item.label, value)}
|
||||
selectionColor={Theme.Colors.Cursor}
|
||||
inputStyle={{
|
||||
textAlign: I18nManager.isRTL ? 'right' : 'left',
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import React from 'react';
|
||||
import {Icon, ListItem, Overlay} from 'react-native-elements';
|
||||
import {Theme} from '../components/ui/styleUtils';
|
||||
import {Column, Row, Text} from '../components/ui';
|
||||
@@ -11,6 +10,7 @@ import {useTranslation} from 'react-i18next';
|
||||
import {HistoryTab} from '../screens/Home/MyVcs/HistoryTab';
|
||||
import {RemoveVcWarningOverlay} from '../screens/Home/MyVcs/RemoveVcWarningOverlay';
|
||||
import {ScrollView} from 'react-native-gesture-handler';
|
||||
import {VCMetadata} from '../shared/VCMetadata';
|
||||
import testIDProps from '../shared/commonUtil';
|
||||
|
||||
export const KebabPopUp: React.FC<KebabPopUpProps> = props => {
|
||||
@@ -46,9 +46,7 @@ export const KebabPopUp: React.FC<KebabPopUpProps> = props => {
|
||||
<ListItem.Title>
|
||||
<Pressable onPress={controller.PIN_CARD}>
|
||||
<Text size="small" weight="bold">
|
||||
{props.vcKey.split(':')[4] == 'true'
|
||||
? t('unPinCard')
|
||||
: t('pinCard')}
|
||||
{props.vcMetadata.isPinned ? t('unPinCard') : t('pinCard')}
|
||||
</Text>
|
||||
</Pressable>
|
||||
</ListItem.Title>
|
||||
@@ -65,13 +63,13 @@ export const KebabPopUp: React.FC<KebabPopUpProps> = props => {
|
||||
testID="viewActivityLog"
|
||||
service={props.service}
|
||||
label={t('viewActivityLog')}
|
||||
vcKey={props.vcKey}
|
||||
vcMetadata={props.vcMetadata}
|
||||
/>
|
||||
|
||||
<ListItem testID="removeFromWallet" bottomDivider>
|
||||
<ListItem.Content>
|
||||
<ListItem.Title>
|
||||
<Pressable onPress={() => controller.REMOVE(props.vcKey)}>
|
||||
<Pressable onPress={() => controller.REMOVE(props.vcMetadata)}>
|
||||
<Text size="small" weight="bold">
|
||||
{t('removeFromWallet')}
|
||||
</Text>
|
||||
@@ -94,7 +92,7 @@ export const KebabPopUp: React.FC<KebabPopUpProps> = props => {
|
||||
export interface KebabPopUpProps {
|
||||
iconName: string;
|
||||
iconType?: string;
|
||||
vcKey: string;
|
||||
vcMetadata: VCMetadata;
|
||||
isVisible: boolean;
|
||||
onDismiss: () => void;
|
||||
service: ActorRefFrom<typeof vcItemMachine>;
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
import { selectActivities } from '../machines/activityLog';
|
||||
import { GlobalContext } from '../shared/GlobalContext';
|
||||
import { useContext } from 'react';
|
||||
import { VCMetadata } from '../shared/VCMetadata';
|
||||
|
||||
export function useKebabPopUp(props) {
|
||||
const service = props.service as ActorRefFrom<typeof vcItemMachine>;
|
||||
@@ -26,7 +27,8 @@ export function useKebabPopUp(props) {
|
||||
const ADD_WALLET_BINDING_ID = () =>
|
||||
service.send(VcItemEvents.ADD_WALLET_BINDING_ID());
|
||||
const CONFIRM = () => service.send(VcItemEvents.CONFIRM());
|
||||
const REMOVE = (vcKey: string) => service.send(VcItemEvents.REMOVE(vcKey));
|
||||
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());
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, {useContext, useRef} from 'react';
|
||||
import React, {useContext, useEffect, useRef} from 'react';
|
||||
import {useInterpret, useSelector} from '@xstate/react';
|
||||
import {Pressable} from 'react-native';
|
||||
import {ActorRefFrom} from 'xstate';
|
||||
@@ -22,18 +22,23 @@ import {VcItemActivationStatus} from './VcItemActivationStatus';
|
||||
import {Row} from './ui';
|
||||
import {KebabPopUp} from './KebabPopUp';
|
||||
import {logState} from '../machines/app';
|
||||
import {VCMetadata} from '../shared/VCMetadata';
|
||||
|
||||
export const VcItem: React.FC<VcItemProps> = props => {
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const machine = useRef(
|
||||
createVcItemMachine(
|
||||
appService.getSnapshot().context.serviceRefs,
|
||||
props.vcKey,
|
||||
props.vcMetadata,
|
||||
),
|
||||
);
|
||||
|
||||
const service = useInterpret(machine.current, {devTools: __DEV__});
|
||||
|
||||
useEffect(() => {
|
||||
service.subscribe(logState);
|
||||
}, [service]);
|
||||
|
||||
const context = useSelector(service, selectContext);
|
||||
const verifiableCredential = useSelector(service, selectVerifiableCredential);
|
||||
const emptyWalletBindingId = useSelector(service, selectEmptyWalletBindingId);
|
||||
@@ -82,7 +87,7 @@ export const VcItem: React.FC<VcItemProps> = props => {
|
||||
)}
|
||||
<Pressable onPress={KEBAB_POPUP}>
|
||||
<KebabPopUp
|
||||
vcKey={props.vcKey}
|
||||
vcMetadata={props.vcMetadata}
|
||||
iconName="dots-three-horizontal"
|
||||
iconType="entypo"
|
||||
isVisible={isKebabPopUp}
|
||||
@@ -104,7 +109,7 @@ export const VcItem: React.FC<VcItemProps> = props => {
|
||||
};
|
||||
|
||||
interface VcItemProps {
|
||||
vcKey: string;
|
||||
vcMetadata: VCMetadata;
|
||||
margin?: string;
|
||||
selectable?: boolean;
|
||||
selected?: boolean;
|
||||
|
||||
@@ -16,13 +16,14 @@ 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);
|
||||
const machine = useRef(
|
||||
createVcItemMachine(
|
||||
appService.getSnapshot().context.serviceRefs,
|
||||
props.vcKey
|
||||
props.vcMetadata
|
||||
)
|
||||
);
|
||||
const service = useInterpret(machine.current);
|
||||
@@ -99,7 +100,7 @@ export const VidItem: React.FC<VcItemProps> = (props) => {
|
||||
};
|
||||
|
||||
interface VcItemProps {
|
||||
vcKey: string;
|
||||
vcMetadata: VCMetadata;
|
||||
margin?: string;
|
||||
selectable?: boolean;
|
||||
selected?: boolean;
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Text } from './Text';
|
||||
import {Theme, Spacing} from './styleUtils';
|
||||
import testIDProps from '../../shared/commonUtil';
|
||||
|
||||
export const Button: React.FC<ButtonProps> = (props) => {
|
||||
export const Button: React.FC<ButtonProps> = props => {
|
||||
const type = props.type || 'solid' || 'radius' || 'gradient';
|
||||
const buttonStyle: StyleProp<ViewStyle> = [
|
||||
props.fill ? Theme.ButtonStyles.fill : null,
|
||||
|
||||
@@ -15,7 +15,7 @@ import testIDProps from '../../shared/commonUtil';
|
||||
function createLayout(
|
||||
direction: FlexStyle['flexDirection'],
|
||||
mainAlign?: FlexStyle['justifyContent'],
|
||||
crossAlign?: FlexStyle['alignItems']
|
||||
crossAlign?: FlexStyle['alignItems'],
|
||||
) {
|
||||
const layoutStyles = StyleSheet.create({
|
||||
base: {
|
||||
@@ -28,7 +28,7 @@ function createLayout(
|
||||
},
|
||||
});
|
||||
|
||||
const Layout: React.FC<LayoutProps> = (props) => {
|
||||
const Layout: React.FC<LayoutProps> = props => {
|
||||
const styles: StyleProp<ViewStyle> = [
|
||||
layoutStyles.base,
|
||||
props.fill ? layoutStyles.fill : null,
|
||||
@@ -73,7 +73,7 @@ export const Centered = createLayout('column', 'center', 'center');
|
||||
export const HorizontallyCentered = createLayout(
|
||||
'column',
|
||||
'flex-start',
|
||||
'center'
|
||||
'center',
|
||||
);
|
||||
|
||||
interface LayoutProps {
|
||||
|
||||
6
i18n.ts
6
i18n.ts
@@ -59,7 +59,7 @@ export function getLanguageCode(code: string) {
|
||||
export function getVCDetailsForCurrentLanguage(locales) {
|
||||
const currentLanguage = i18next.language;
|
||||
const vcDetailsForCurrentLanguage = locales.filter(
|
||||
(obj) => obj.language === languageCodeMap[currentLanguage]
|
||||
obj => obj.language === languageCodeMap[currentLanguage],
|
||||
);
|
||||
return vcDetailsForCurrentLanguage[0]?.value
|
||||
? vcDetailsForCurrentLanguage[0].value
|
||||
@@ -70,13 +70,13 @@ export function getVCDetailsForCurrentLanguage(locales) {
|
||||
// The response received from the server is three letter language code and the value in the inji code base is two letter language code. Hence the conversion is done.
|
||||
function getThreeLetterLanguageCode(twoLetterLanguageCode) {
|
||||
return Object.keys(iso6393To1).find(
|
||||
(key) => iso6393To1[key] === twoLetterLanguageCode
|
||||
key => iso6393To1[key] === twoLetterLanguageCode,
|
||||
);
|
||||
}
|
||||
|
||||
function populateLanguageCodeMap() {
|
||||
const supportedLanguages = Object.keys(SUPPORTED_LANGUAGES);
|
||||
supportedLanguages.forEach((languageCode) => {
|
||||
supportedLanguages.forEach(languageCode => {
|
||||
let threeLetterLanguageCode = languageCode;
|
||||
|
||||
if (isTwoLetterLanguageCode(languageCode)) {
|
||||
|
||||
@@ -281,6 +281,7 @@
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXUpdates.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GNSSharedResources.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Assets.car",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
||||
@@ -762,7 +762,7 @@ SPEC CHECKSUMS:
|
||||
BVLinearGradient: 916632041121a658c704df89d99f04acb038de0f
|
||||
CatCrypto: a477899b6be4954e75be4897e732da098cc0a5a8
|
||||
CrcSwift: f85dea6b41dddb5f98bb3743fd777ce58b77bc2e
|
||||
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
|
||||
DoubleConversion: cf9b38bf0b2d048436d9a82ad2abe1404f11e7de
|
||||
EASClient: 950674e1098ebc09c4c2cf064a61e42e84d9d4c6
|
||||
EXApplication: d8f53a7eee90a870a75656280e8d4b85726ea903
|
||||
EXBarCodeScanner: 8e23fae8d267dbef9f04817833a494200f1fce35
|
||||
@@ -785,7 +785,7 @@ SPEC CHECKSUMS:
|
||||
FBLazyVector: f637f31eacba90d4fdeff3fa41608b8f361c173b
|
||||
FBReactNativeSpec: 0d9a4f4de7ab614c49e98c00aedfd3bfbda33d59
|
||||
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
|
||||
glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b
|
||||
glog: 73c2498ac6884b13ede40eda8228cb1eee9d9d62
|
||||
GoogleInterchangeUtilities: d5bc4d88d5b661ab72f9d70c58d02ca8c27ad1f7
|
||||
GoogleNetworkingUtilities: 3edd3a8161347494f2da60ea0deddc8a472d94cb
|
||||
GoogleSymbolUtilities: 631ee17048aa5e9ab133470d768ea997a5ef9b96
|
||||
@@ -855,4 +855,4 @@ SPEC CHECKSUMS:
|
||||
|
||||
PODFILE CHECKSUM: 01f58b130fa221dabb14b2d82d981ef24dcaba53
|
||||
|
||||
COCOAPODS: 1.12.1
|
||||
COCOAPODS: 1.11.3
|
||||
|
||||
@@ -12,15 +12,13 @@ import { MY_VCS_STORE_KEY, ESIGNET_BASE_URL } from '../shared/constants';
|
||||
import {StoreEvents} from './store';
|
||||
import {linkTransactionResponse, VC} from '../types/vc';
|
||||
import {request} from '../shared/request';
|
||||
import {
|
||||
getJwt,
|
||||
isCustomSecureKeystore,
|
||||
} from '../shared/cryptoutil/cryptoUtil';
|
||||
import {getJwt, isCustomSecureKeystore} from '../shared/cryptoutil/cryptoUtil';
|
||||
import {
|
||||
getBindingCertificateConstant,
|
||||
getPrivateKey,
|
||||
} from '../shared/keystore/SecureKeystore';
|
||||
import i18n from '../i18n';
|
||||
import {parseMetadatas, VCMetadata} from '../shared/VCMetadata';
|
||||
import {getEndData, sendEndEvent} from '../shared/telemetry/TelemetryUtils';
|
||||
|
||||
const model = createModel(
|
||||
@@ -28,7 +26,7 @@ const model = createModel(
|
||||
serviceRefs: {} as AppServices,
|
||||
selectedVc: {} as VC,
|
||||
linkCode: '',
|
||||
myVcs: [] as string[],
|
||||
myVcs: [] as VCMetadata[],
|
||||
thumbprint: '',
|
||||
linkTransactionResponse: {} as linkTransactionResponse,
|
||||
authFactors: [],
|
||||
@@ -65,7 +63,7 @@ const model = createModel(
|
||||
FACE_INVALID: () => ({}),
|
||||
RETRY_VERIFICATION: () => ({}),
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export const QrLoginEvents = model.events;
|
||||
@@ -235,7 +233,7 @@ export const qrLoginMachine =
|
||||
},
|
||||
done: {
|
||||
type: 'final',
|
||||
data: (context) => context,
|
||||
data: context => context,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -247,22 +245,24 @@ export const qrLoginMachine =
|
||||
linkCode: (context, event) => event.value,
|
||||
}),
|
||||
|
||||
// TODO: loaded VCMetadatas are not used anywhere. remove?
|
||||
loadMyVcs: send(StoreEvents.GET(MY_VCS_STORE_KEY), {
|
||||
to: (context) => context.serviceRefs.store,
|
||||
to: context => context.serviceRefs.store,
|
||||
}),
|
||||
|
||||
setMyVcs: model.assign({
|
||||
myVcs: (_context, event) => (event.response || []) as string[],
|
||||
myVcs: (_context, event) =>
|
||||
parseMetadatas((event.response || []) as object[]),
|
||||
}),
|
||||
|
||||
loadThumbprint: send(
|
||||
(context) =>
|
||||
context =>
|
||||
StoreEvents.GET(
|
||||
getBindingCertificateConstant(
|
||||
context.selectedVc.walletBindingResponse?.walletBindingId
|
||||
)
|
||||
context.selectedVc.walletBindingResponse?.walletBindingId,
|
||||
),
|
||||
{ to: (context) => context.serviceRefs.store }
|
||||
),
|
||||
{to: context => context.serviceRefs.store},
|
||||
),
|
||||
setThumbprint: assign({
|
||||
thumbprint: (_context, event) => {
|
||||
@@ -289,29 +289,29 @@ export const qrLoginMachine =
|
||||
}),
|
||||
|
||||
expandLinkTransResp: assign({
|
||||
authFactors: (context) => context.linkTransactionResponse.authFactors,
|
||||
authFactors: context => context.linkTransactionResponse.authFactors,
|
||||
|
||||
authorizeScopes: (context) =>
|
||||
authorizeScopes: context =>
|
||||
context.linkTransactionResponse.authorizeScopes,
|
||||
|
||||
clientName: (context) => context.linkTransactionResponse.clientName,
|
||||
clientName: context => context.linkTransactionResponse.clientName,
|
||||
|
||||
configs: (context) => context.linkTransactionResponse.configs,
|
||||
configs: context => context.linkTransactionResponse.configs,
|
||||
|
||||
essentialClaims: (context) =>
|
||||
essentialClaims: context =>
|
||||
context.linkTransactionResponse.essentialClaims,
|
||||
|
||||
linkTransactionId: (context) =>
|
||||
linkTransactionId: context =>
|
||||
context.linkTransactionResponse.linkTransactionId,
|
||||
|
||||
logoUrl: (context) => context.linkTransactionResponse.logoUrl,
|
||||
logoUrl: context => context.linkTransactionResponse.logoUrl,
|
||||
|
||||
voluntaryClaims: (context) =>
|
||||
voluntaryClaims: context =>
|
||||
context.linkTransactionResponse.voluntaryClaims,
|
||||
}),
|
||||
|
||||
setClaims: (context) => {
|
||||
context.voluntaryClaims.map((claim) => {
|
||||
setClaims: context => {
|
||||
context.voluntaryClaims.map(claim => {
|
||||
context.isSharing[claim] = false;
|
||||
});
|
||||
},
|
||||
@@ -331,7 +331,7 @@ export const qrLoginMachine =
|
||||
} else {
|
||||
context.selectedVoluntaryClaims =
|
||||
context.selectedVoluntaryClaims.filter(
|
||||
(eachClaim) => eachClaim !== event.claim
|
||||
eachClaim => eachClaim !== event.claim,
|
||||
);
|
||||
}
|
||||
return {...context.isSharing};
|
||||
@@ -342,7 +342,7 @@ export const qrLoginMachine =
|
||||
}),
|
||||
},
|
||||
services: {
|
||||
linkTransaction: async (context) => {
|
||||
linkTransaction: async context => {
|
||||
const response = await request(
|
||||
'POST',
|
||||
'/v1/esignet/linked-authorization/link-transaction',
|
||||
@@ -352,17 +352,17 @@ export const qrLoginMachine =
|
||||
linkCode: context.linkCode,
|
||||
},
|
||||
},
|
||||
ESIGNET_BASE_URL
|
||||
ESIGNET_BASE_URL,
|
||||
);
|
||||
return response.response;
|
||||
},
|
||||
|
||||
sendAuthenticate: async (context) => {
|
||||
sendAuthenticate: async context => {
|
||||
let privateKey;
|
||||
|
||||
if (!isCustomSecureKeystore()) {
|
||||
privateKey = await getPrivateKey(
|
||||
context.selectedVc.walletBindingResponse?.walletBindingId
|
||||
context.selectedVc.walletBindingResponse?.walletBindingId,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -370,7 +370,7 @@ export const qrLoginMachine =
|
||||
var jwt = await getJwt(
|
||||
privateKey,
|
||||
context.selectedVc.id,
|
||||
context.thumbprint
|
||||
context.thumbprint,
|
||||
);
|
||||
|
||||
const response = await request(
|
||||
@@ -390,23 +390,23 @@ export const qrLoginMachine =
|
||||
],
|
||||
},
|
||||
},
|
||||
ESIGNET_BASE_URL
|
||||
ESIGNET_BASE_URL,
|
||||
);
|
||||
return response.response.linkedTransactionId;
|
||||
},
|
||||
|
||||
sendConsent: async (context) => {
|
||||
sendConsent: async context => {
|
||||
let privateKey;
|
||||
if (!isCustomSecureKeystore()) {
|
||||
privateKey = await getPrivateKey(
|
||||
context.selectedVc.walletBindingResponse?.walletBindingId
|
||||
context.selectedVc.walletBindingResponse?.walletBindingId,
|
||||
);
|
||||
}
|
||||
|
||||
const jwt = await getJwt(
|
||||
privateKey,
|
||||
context.selectedVc.id,
|
||||
context.thumbprint
|
||||
context.thumbprint,
|
||||
);
|
||||
|
||||
const response = await request(
|
||||
@@ -426,7 +426,7 @@ export const qrLoginMachine =
|
||||
],
|
||||
},
|
||||
},
|
||||
ESIGNET_BASE_URL
|
||||
ESIGNET_BASE_URL,
|
||||
);
|
||||
var linkedTrnId = response.response.linkedTransactionId;
|
||||
|
||||
@@ -438,17 +438,17 @@ export const qrLoginMachine =
|
||||
request: {
|
||||
linkedTransactionId: linkedTrnId,
|
||||
acceptedClaims: context.essentialClaims.concat(
|
||||
context.selectedVoluntaryClaims
|
||||
context.selectedVoluntaryClaims,
|
||||
),
|
||||
permittedAuthorizeScopes: context.authorizeScopes,
|
||||
},
|
||||
},
|
||||
ESIGNET_BASE_URL
|
||||
ESIGNET_BASE_URL,
|
||||
);
|
||||
console.log(resp.response.linkedTransactionId);
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export function createQrLoginMachine(serviceRefs: AppServices) {
|
||||
|
||||
@@ -55,7 +55,7 @@ const model = createModel(
|
||||
STORE_RESPONSE: (response: unknown) => ({response}),
|
||||
RESET_KEY_INVALIDATE_ERROR_DISMISS: () => ({}),
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export const APP_EVENTS = model.events;
|
||||
@@ -208,9 +208,9 @@ export const appMachine = model.createMachine(
|
||||
{
|
||||
actions: {
|
||||
forwardToServices: pure((context, event) =>
|
||||
Object.values(context.serviceRefs).map((serviceRef) =>
|
||||
send({ ...event, type: `APP_${event.type}` }, { to: serviceRef })
|
||||
)
|
||||
Object.values(context.serviceRefs).map(serviceRef =>
|
||||
send({...event, type: `APP_${event.type}`}, {to: serviceRef}),
|
||||
),
|
||||
),
|
||||
setIsReadError: assign({
|
||||
isReadError: true,
|
||||
@@ -237,7 +237,7 @@ export const appMachine = model.createMachine(
|
||||
isKeyInvalidateError: false,
|
||||
}),
|
||||
|
||||
requestDeviceInfo: respond((context) => ({
|
||||
requestDeviceInfo: respond(context => ({
|
||||
type: 'RECEIVE_DEVICE_INFO',
|
||||
info: {
|
||||
...context.info,
|
||||
@@ -246,63 +246,63 @@ export const appMachine = model.createMachine(
|
||||
})),
|
||||
|
||||
spawnStoreActor: assign({
|
||||
serviceRefs: (context) => ({
|
||||
serviceRefs: context => ({
|
||||
...context.serviceRefs,
|
||||
store: spawn(storeMachine, storeMachine.id),
|
||||
}),
|
||||
}),
|
||||
|
||||
logStoreEvents: (context) => {
|
||||
logStoreEvents: context => {
|
||||
if (__DEV__) {
|
||||
context.serviceRefs.store.subscribe(logState);
|
||||
}
|
||||
},
|
||||
|
||||
spawnServiceActors: model.assign({
|
||||
serviceRefs: (context) => {
|
||||
serviceRefs: context => {
|
||||
const serviceRefs = {
|
||||
...context.serviceRefs,
|
||||
};
|
||||
|
||||
serviceRefs.auth = spawn(
|
||||
createAuthMachine(serviceRefs),
|
||||
authMachine.id
|
||||
authMachine.id,
|
||||
);
|
||||
|
||||
serviceRefs.vc = spawn(createVcMachine(serviceRefs), vcMachine.id);
|
||||
|
||||
serviceRefs.settings = spawn(
|
||||
createSettingsMachine(serviceRefs),
|
||||
settingsMachine.id
|
||||
settingsMachine.id,
|
||||
);
|
||||
|
||||
serviceRefs.activityLog = spawn(
|
||||
createActivityLogMachine(serviceRefs),
|
||||
activityLogMachine.id
|
||||
activityLogMachine.id,
|
||||
);
|
||||
|
||||
serviceRefs.scan = spawn(
|
||||
createScanMachine(serviceRefs),
|
||||
scanMachine.id
|
||||
scanMachine.id,
|
||||
);
|
||||
|
||||
if (Platform.OS === 'android') {
|
||||
serviceRefs.request = spawn(
|
||||
createRequestMachine(serviceRefs),
|
||||
requestMachine.id
|
||||
requestMachine.id,
|
||||
);
|
||||
}
|
||||
|
||||
serviceRefs.revoke = spawn(
|
||||
createRevokeMachine(serviceRefs),
|
||||
revokeVidsMachine.id
|
||||
revokeVidsMachine.id,
|
||||
);
|
||||
|
||||
return serviceRefs;
|
||||
},
|
||||
}),
|
||||
|
||||
logServiceEvents: (context) => {
|
||||
logServiceEvents: context => {
|
||||
if (__DEV__) {
|
||||
context.serviceRefs.auth.subscribe(logState);
|
||||
context.serviceRefs.vc.subscribe(logState);
|
||||
@@ -329,19 +329,19 @@ export const appMachine = model.createMachine(
|
||||
loadCredentialRegistryHostFromStorage: send(
|
||||
StoreEvents.GET(SETTINGS_STORE_KEY),
|
||||
{
|
||||
to: (context) => context.serviceRefs.store,
|
||||
}
|
||||
to: context => context.serviceRefs.store,
|
||||
},
|
||||
),
|
||||
|
||||
loadEsignetHostFromStorage: send(StoreEvents.GET(SETTINGS_STORE_KEY), {
|
||||
to: (context) => context.serviceRefs.store,
|
||||
to: context => context.serviceRefs.store,
|
||||
}),
|
||||
|
||||
loadCredentialRegistryInConstants: (_context, event) => {
|
||||
changeCrendetialRegistry(
|
||||
!event.response?.credentialRegistry
|
||||
? MIMOTO_BASE_URL
|
||||
: event.response?.credentialRegistry
|
||||
: event.response?.credentialRegistry,
|
||||
);
|
||||
},
|
||||
|
||||
@@ -349,13 +349,13 @@ export const appMachine = model.createMachine(
|
||||
changeEsignetUrl(
|
||||
!event.response?.esignetHostUrl
|
||||
? ESIGNET_BASE_URL
|
||||
: event.response?.esignetHostUrl
|
||||
: event.response?.esignetHostUrl,
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
services: {
|
||||
getAppInfo: () => async (callback) => {
|
||||
getAppInfo: () => async callback => {
|
||||
const appInfo = {
|
||||
deviceId: getDeviceId(),
|
||||
deviceName: await getDeviceName(),
|
||||
@@ -363,7 +363,7 @@ export const appMachine = model.createMachine(
|
||||
callback(model.events.APP_INFO_RECEIVED(appInfo));
|
||||
},
|
||||
|
||||
getBackendInfo: () => async (callback) => {
|
||||
getBackendInfo: () => async callback => {
|
||||
let backendInfo = {
|
||||
application: {
|
||||
name: '',
|
||||
@@ -380,7 +380,7 @@ export const appMachine = model.createMachine(
|
||||
}
|
||||
},
|
||||
|
||||
checkFocusState: () => (callback) => {
|
||||
checkFocusState: () => callback => {
|
||||
const changeHandler = (newState: AppStateStatus) => {
|
||||
switch (newState) {
|
||||
case 'background':
|
||||
@@ -414,8 +414,8 @@ export const appMachine = model.createMachine(
|
||||
};
|
||||
},
|
||||
|
||||
checkNetworkState: () => (callback) => {
|
||||
return NetInfo.addEventListener((state) => {
|
||||
checkNetworkState: () => callback => {
|
||||
return NetInfo.addEventListener(state => {
|
||||
if (state.isConnected) {
|
||||
callback({type: 'ONLINE', networkType: state.type});
|
||||
} else {
|
||||
@@ -424,7 +424,7 @@ export const appMachine = model.createMachine(
|
||||
});
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
interface AppInfo {
|
||||
@@ -477,7 +477,7 @@ export function logState(state: AnyState) {
|
||||
}
|
||||
return value;
|
||||
},
|
||||
2
|
||||
2,
|
||||
);
|
||||
console.log(
|
||||
`[${getDeviceNameSync()}] ${state.machine.id}: ${
|
||||
@@ -485,7 +485,7 @@ export function logState(state: AnyState) {
|
||||
} -> ${state.toStrings().pop()}\n${
|
||||
data.length > 300 ? data.slice(0, 300) + '...' : data
|
||||
}
|
||||
`
|
||||
`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,22 +2,22 @@
|
||||
|
||||
export interface Typegen0 {
|
||||
'@@xstate/typegen': true;
|
||||
'internalEvents': {
|
||||
internalEvents: {
|
||||
'xstate.init': {type: 'xstate.init'};
|
||||
};
|
||||
'invokeSrcNameMap': {
|
||||
invokeSrcNameMap: {
|
||||
checkFocusState: 'done.invoke.app.ready.focus:invocation[0]';
|
||||
checkNetworkState: 'done.invoke.app.ready.network:invocation[0]';
|
||||
getAppInfo: 'done.invoke.app.init.info:invocation[0]';
|
||||
getBackendInfo: 'done.invoke.app.init.devinfo:invocation[0]';
|
||||
};
|
||||
'missingImplementations': {
|
||||
missingImplementations: {
|
||||
actions: never;
|
||||
delays: never;
|
||||
guards: never;
|
||||
services: never;
|
||||
};
|
||||
'eventsCausingActions': {
|
||||
eventsCausingActions: {
|
||||
forwardToServices: 'ACTIVE' | 'INACTIVE' | 'OFFLINE' | 'ONLINE';
|
||||
loadCredentialRegistryHostFromStorage: 'READY';
|
||||
loadCredentialRegistryInConstants: 'STORE_RESPONSE';
|
||||
@@ -41,15 +41,15 @@ export interface Typegen0 {
|
||||
unsetIsReadError: 'READY';
|
||||
updateKeyInvalidateError: 'ERROR' | 'KEY_INVALIDATE_ERROR';
|
||||
};
|
||||
'eventsCausingDelays': {};
|
||||
'eventsCausingGuards': {};
|
||||
'eventsCausingServices': {
|
||||
eventsCausingDelays: {};
|
||||
eventsCausingGuards: {};
|
||||
eventsCausingServices: {
|
||||
checkFocusState: 'BACKEND_INFO_RECEIVED';
|
||||
checkNetworkState: 'BACKEND_INFO_RECEIVED';
|
||||
getAppInfo: 'STORE_RESPONSE';
|
||||
getBackendInfo: 'APP_INFO_RECEIVED';
|
||||
};
|
||||
'matchesStates':
|
||||
matchesStates:
|
||||
| 'init'
|
||||
| 'init.credentialRegistry'
|
||||
| 'init.devinfo'
|
||||
@@ -76,5 +76,5 @@ export interface Typegen0 {
|
||||
network?: 'checking' | 'offline' | 'online';
|
||||
};
|
||||
};
|
||||
'tags': never;
|
||||
tags: never;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ const model = createModel(
|
||||
SELECT: () => ({}),
|
||||
NEXT: () => ({}),
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export const AuthEvents = model.events;
|
||||
@@ -131,15 +131,15 @@ export const authMachine = model.createMachine(
|
||||
{
|
||||
actions: {
|
||||
requestStoredContext: send(StoreEvents.GET('auth'), {
|
||||
to: (context) => context.serviceRefs.store,
|
||||
to: context => context.serviceRefs.store,
|
||||
}),
|
||||
|
||||
storeContext: send(
|
||||
(context) => {
|
||||
context => {
|
||||
const {serviceRefs, ...data} = context;
|
||||
return StoreEvents.SET('auth', data);
|
||||
},
|
||||
{ to: (context) => context.serviceRefs.store }
|
||||
{to: context => context.serviceRefs.store},
|
||||
),
|
||||
|
||||
setContext: model.assign((_, event) => {
|
||||
@@ -158,7 +158,7 @@ export const authMachine = model.createMachine(
|
||||
}),
|
||||
|
||||
setLanguage: assign({
|
||||
selectLanguage: (context) => true,
|
||||
selectLanguage: context => true,
|
||||
}),
|
||||
|
||||
setPasscodeSalt: assign({
|
||||
@@ -172,7 +172,7 @@ export const authMachine = model.createMachine(
|
||||
downloadFaceSdkModel: () => () => {
|
||||
downloadModel();
|
||||
},
|
||||
generatePasscodeSalt: () => async (context) => {
|
||||
generatePasscodeSalt: () => async context => {
|
||||
const randomBytes = await generateSecureRandom(16);
|
||||
return binaryToBase64(randomBytes) as string;
|
||||
},
|
||||
@@ -181,17 +181,17 @@ export const authMachine = model.createMachine(
|
||||
guards: {
|
||||
hasData: (_, event: StoreResponseEvent) => event.response != null,
|
||||
|
||||
hasPasscodeSet: (context) => {
|
||||
hasPasscodeSet: context => {
|
||||
return context.passcode !== '';
|
||||
},
|
||||
hasBiometricSet: (context) => {
|
||||
hasBiometricSet: context => {
|
||||
return context.biometrics !== '' && context.passcode !== '';
|
||||
},
|
||||
hasLanguageset: (context) => {
|
||||
hasLanguageset: context => {
|
||||
return !context.selectLanguage;
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export function createAuthMachine(serviceRefs: AppServices) {
|
||||
|
||||
@@ -14,10 +14,7 @@ import {getDeviceNameSync} from 'react-native-device-info';
|
||||
import {StoreEvents} from '../../store';
|
||||
import {VC} from '../../../types/vc';
|
||||
import {AppServices} from '../../../shared/GlobalContext';
|
||||
import {
|
||||
RECEIVED_VCS_STORE_KEY,
|
||||
VC_ITEM_STORE_KEY,
|
||||
} from '../../../shared/constants';
|
||||
import {RECEIVED_VCS_STORE_KEY} from '../../../shared/constants';
|
||||
import {ActivityLogEvents, ActivityLogType} from '../../activityLog';
|
||||
import {VcEvents} from '../../vc';
|
||||
import {subscribe} from '../../../shared/openIdBLE/verifierEventHandler';
|
||||
@@ -25,6 +22,7 @@ import {log} from 'xstate/lib/actions';
|
||||
import {VerifierDataEvent} from 'react-native-tuvali/src/types/events';
|
||||
import {BLEError} from '../types';
|
||||
import Storage from '../../../shared/storage';
|
||||
import {VCMetadata} from '../../../shared/VCMetadata';
|
||||
// import { verifyPresentation } from '../shared/vcjs/verifyPresentation';
|
||||
|
||||
const {verifier, EventTypes, VerificationStatus} = tuvali;
|
||||
@@ -67,7 +65,7 @@ const model = createModel(
|
||||
STORE_ERROR: (error: Error) => ({error}),
|
||||
RECEIVE_DEVICE_INFO: (info: DeviceInfo) => ({info}),
|
||||
RECEIVED_VCS_UPDATED: () => ({}),
|
||||
VC_RESPONSE: (response: unknown) => ({response}),
|
||||
VC_RESPONSE: (vcMetadatas: VCMetadata[]) => ({vcMetadatas}),
|
||||
GOTO_SETTINGS: () => ({}),
|
||||
APP_ACTIVE: () => ({}),
|
||||
FACE_VALID: () => ({}),
|
||||
@@ -584,13 +582,14 @@ export const requestMachine =
|
||||
context =>
|
||||
StoreEvents.PREPEND(
|
||||
RECEIVED_VCS_STORE_KEY,
|
||||
VC_ITEM_STORE_KEY(context.incomingVc),
|
||||
VCMetadata.fromVC(context.incomingVc),
|
||||
),
|
||||
{to: context => context.serviceRefs.store},
|
||||
),
|
||||
|
||||
requestExistingVc: send(
|
||||
context => StoreEvents.GET(VC_ITEM_STORE_KEY(context.incomingVc)),
|
||||
context =>
|
||||
StoreEvents.GET(VCMetadata.fromVC(context.incomingVc).getVcKey()),
|
||||
{to: context => context.serviceRefs.store},
|
||||
),
|
||||
|
||||
@@ -601,7 +600,10 @@ export const requestMachine =
|
||||
...existing,
|
||||
reason: existing.reason.concat(context.incomingVc.reason),
|
||||
};
|
||||
return StoreEvents.SET(VC_ITEM_STORE_KEY(updated), updated);
|
||||
return StoreEvents.SET(
|
||||
VCMetadata.fromVC(updated).getVcKey(),
|
||||
updated,
|
||||
);
|
||||
},
|
||||
{to: context => context.serviceRefs.store},
|
||||
),
|
||||
@@ -609,7 +611,7 @@ export const requestMachine =
|
||||
storeVc: send(
|
||||
context =>
|
||||
StoreEvents.SET(
|
||||
VC_ITEM_STORE_KEY(context.incomingVc),
|
||||
VCMetadata.fromVC(context.incomingVc).getVcKey(),
|
||||
context.incomingVc,
|
||||
),
|
||||
{to: context => context.serviceRefs.store},
|
||||
@@ -634,7 +636,7 @@ export const requestMachine =
|
||||
logReceived: send(
|
||||
context =>
|
||||
ActivityLogEvents.LOG_ACTIVITY({
|
||||
_vcKey: VC_ITEM_STORE_KEY(context.incomingVc),
|
||||
_vcKey: VCMetadata.fromVC(context.incomingVc).getVcKey(),
|
||||
type: context.receiveLogType,
|
||||
timestamp: Date.now(),
|
||||
deviceName:
|
||||
@@ -646,7 +648,7 @@ export const requestMachine =
|
||||
|
||||
sendVcReceived: send(
|
||||
context => {
|
||||
return VcEvents.VC_RECEIVED(VC_ITEM_STORE_KEY(context.incomingVc));
|
||||
return VcEvents.VC_RECEIVED(VCMetadata.fromVC(context.incomingVc));
|
||||
},
|
||||
{to: context => context.serviceRefs.vc},
|
||||
),
|
||||
@@ -805,9 +807,12 @@ export const requestMachine =
|
||||
|
||||
guards: {
|
||||
hasExistingVc: (context, event) => {
|
||||
const receivedVcs = event.response as string[];
|
||||
const vcKey = VC_ITEM_STORE_KEY(context.incomingVc);
|
||||
return receivedVcs.includes(vcKey);
|
||||
const receivedVcs = event.vcMetadatas;
|
||||
const incomingVcMetadata = VCMetadata.fromVC(context.incomingVc);
|
||||
return receivedVcs?.some(
|
||||
vcMetadata =>
|
||||
vcMetadata.getVcKey() == incomingVcMetadata.getVcKey(),
|
||||
);
|
||||
},
|
||||
|
||||
isMinimumStorageLimitReached: (_context, event) => Boolean(event.data),
|
||||
|
||||
@@ -17,7 +17,7 @@ import {getDeviceNameSync} from 'react-native-device-info';
|
||||
import {VC, VerifiablePresentation} from '../../../types/vc';
|
||||
import {AppServices} from '../../../shared/GlobalContext';
|
||||
import {ActivityLogEvents, ActivityLogType} from '../../activityLog';
|
||||
import {MY_LOGIN_STORE_KEY, VC_ITEM_STORE_KEY} from '../../../shared/constants';
|
||||
import {MY_LOGIN_STORE_KEY} from '../../../shared/constants';
|
||||
import {subscribe} from '../../../shared/openIdBLE/walletEventHandler';
|
||||
import {
|
||||
check,
|
||||
@@ -39,6 +39,7 @@ import {WalletDataEvent} from 'react-native-tuvali/lib/typescript/types/events';
|
||||
import {BLEError} from '../types';
|
||||
import Storage from '../../../shared/storage';
|
||||
import {logState} from '../../app';
|
||||
import {VCMetadata} from '../../../shared/VCMetadata';
|
||||
import {
|
||||
getData,
|
||||
getEndData,
|
||||
@@ -794,7 +795,7 @@ export const scanMachine =
|
||||
logShared: send(
|
||||
context =>
|
||||
ActivityLogEvents.LOG_ACTIVITY({
|
||||
_vcKey: VC_ITEM_STORE_KEY(context.selectedVc),
|
||||
_vcKey: VCMetadata.fromVC(context.selectedVc).getVcKey(),
|
||||
type: context.selectedVc.shouldVerifyPresence
|
||||
? 'VC_SHARED_WITH_VERIFICATION_CONSENT'
|
||||
: context.shareLogType,
|
||||
@@ -809,7 +810,7 @@ export const scanMachine =
|
||||
logFailedVerification: send(
|
||||
context =>
|
||||
ActivityLogEvents.LOG_ACTIVITY({
|
||||
_vcKey: VC_ITEM_STORE_KEY(context.selectedVc),
|
||||
_vcKey: VCMetadata.fromVC(context.selectedVc).getVcKey(),
|
||||
type: 'PRESENCE_VERIFICATION_FAILED',
|
||||
timestamp: Date.now(),
|
||||
deviceName:
|
||||
|
||||
@@ -10,6 +10,7 @@ import { createModel } from 'xstate/lib/model';
|
||||
import { request } from '../shared/request';
|
||||
import { VcIdType } from '../types/vc';
|
||||
import { MY_VCS_STORE_KEY } from '../shared/constants';
|
||||
import { VCMetadata } from '../shared/VCMetadata';
|
||||
|
||||
const model = createModel(
|
||||
{
|
||||
@@ -20,7 +21,7 @@ const model = createModel(
|
||||
otpError: '',
|
||||
transactionId: '',
|
||||
requestId: '',
|
||||
VIDs: [] as string[],
|
||||
VIDsMetadata: [] as VCMetadata[],
|
||||
},
|
||||
{
|
||||
events: {
|
||||
@@ -29,7 +30,7 @@ const model = createModel(
|
||||
READY: (idInputRef: TextInput) => ({ idInputRef }),
|
||||
DISMISS: () => ({}),
|
||||
SELECT_ID_TYPE: (idType: VcIdType) => ({ idType }),
|
||||
REVOKE_VCS: (vcKeys: string[]) => ({ vcKeys }),
|
||||
REVOKE_VCS: (vcMetadatas: VCMetadata[]) => ({ vcMetadatas }),
|
||||
STORE_RESPONSE: (response: string[]) => ({ response }),
|
||||
ERROR: (data: Error) => ({ data }),
|
||||
SUCCESS: () => ({}),
|
||||
@@ -165,7 +166,7 @@ export const revokeVidsMachine =
|
||||
}),
|
||||
|
||||
setVIDs: model.assign({
|
||||
VIDs: (_context, event) => event.vcKeys,
|
||||
VIDsMetadata: (_context, event) => event.vcMetadatas,
|
||||
}),
|
||||
|
||||
setIdBackendError: assign({
|
||||
@@ -206,12 +207,12 @@ export const revokeVidsMachine =
|
||||
logRevoked: send(
|
||||
(context) =>
|
||||
ActivityLogEvents.LOG_ACTIVITY(
|
||||
context.VIDs.map((vc) => ({
|
||||
_vcKey: vc,
|
||||
context.VIDsMetadata.map((metadata) => ({
|
||||
_vcKey: metadata.getVcKey(),
|
||||
type: 'VC_REVOKED',
|
||||
timestamp: Date.now(),
|
||||
deviceName: '',
|
||||
vcLabel: vc.split(':')[2],
|
||||
vcLabel: metadata.id,
|
||||
}))
|
||||
),
|
||||
{
|
||||
@@ -221,7 +222,10 @@ export const revokeVidsMachine =
|
||||
|
||||
revokeVID: send(
|
||||
(context) => {
|
||||
return StoreEvents.REMOVE_ITEMS(MY_VCS_STORE_KEY, context.VIDs);
|
||||
return StoreEvents.REMOVE_ITEMS(
|
||||
MY_VCS_STORE_KEY,
|
||||
context.VIDsMetadata.map((m) => m.getVcKey())
|
||||
);
|
||||
},
|
||||
{
|
||||
to: (context) => context.serviceRefs.store,
|
||||
@@ -233,7 +237,7 @@ export const revokeVidsMachine =
|
||||
requestOtp: async (context) => {
|
||||
const transactionId = String(new Date().valueOf()).substring(3, 13);
|
||||
return request('POST', '/residentmobileapp/req/otp', {
|
||||
individualId: context.VIDs[0].split(':')[2],
|
||||
individualId: context.VIDsMetadata[0].id,
|
||||
individualIdType: 'VID',
|
||||
otpChannel: ['EMAIL', 'PHONE'],
|
||||
transactionID: transactionId,
|
||||
@@ -242,20 +246,23 @@ export const revokeVidsMachine =
|
||||
|
||||
requestRevoke: (context) => async (callback) => {
|
||||
await Promise.all(
|
||||
context.VIDs.map((vid: string) => {
|
||||
context.VIDsMetadata.map((metadata: VCMetadata) => {
|
||||
try {
|
||||
const vidID = vid.split(':')[2];
|
||||
const transactionId = String(new Date().valueOf()).substring(
|
||||
3,
|
||||
13
|
||||
);
|
||||
return request('PATCH', `/residentmobileapp/vid/${vidID}`, {
|
||||
return request(
|
||||
'PATCH',
|
||||
`/residentmobileapp/vid/${metadata.id}`,
|
||||
{
|
||||
transactionID: transactionId,
|
||||
vidStatus: 'REVOKED',
|
||||
individualId: vidID,
|
||||
individualId: metadata.id,
|
||||
individualIdType: 'VID',
|
||||
otp: context.otp,
|
||||
});
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
console.log('error.message', error.message);
|
||||
return error;
|
||||
|
||||
@@ -46,7 +46,7 @@ const model = createModel(
|
||||
}),
|
||||
UPDATE_ESIGNET_HOST: (esignetHostUrl: string) => ({esignetHostUrl}),
|
||||
UPDATE_CREDENTIAL_REGISTRY_RESPONSE: (
|
||||
credentialRegistryResponse: string
|
||||
credentialRegistryResponse: string,
|
||||
) => ({
|
||||
credentialRegistryResponse: credentialRegistryResponse,
|
||||
}),
|
||||
@@ -55,7 +55,7 @@ const model = createModel(
|
||||
CANCEL: () => ({}),
|
||||
ACCEPT_HARDWARE_SUPPORT_NOT_EXISTS: () => ({}),
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export const SettingsEvents = model.events;
|
||||
@@ -165,7 +165,7 @@ export const settingsMachine = model.createMachine(
|
||||
{
|
||||
actions: {
|
||||
requestStoredContext: send(StoreEvents.GET(SETTINGS_STORE_KEY), {
|
||||
to: (context) => context.serviceRefs.store,
|
||||
to: context => context.serviceRefs.store,
|
||||
}),
|
||||
|
||||
updateDefaults: model.assign({
|
||||
@@ -179,15 +179,15 @@ export const settingsMachine = model.createMachine(
|
||||
}),
|
||||
|
||||
updatePartialDefaults: model.assign({
|
||||
appId: (context) => context.appId || generateAppId(),
|
||||
appId: context => context.appId || generateAppId(),
|
||||
}),
|
||||
|
||||
storeContext: send(
|
||||
(context) => {
|
||||
context => {
|
||||
const {serviceRefs, ...data} = context;
|
||||
return StoreEvents.SET(SETTINGS_STORE_KEY, data);
|
||||
},
|
||||
{ to: (context) => context.serviceRefs.store }
|
||||
{to: context => context.serviceRefs.store},
|
||||
),
|
||||
|
||||
setContext: model.assign((context, event) => {
|
||||
@@ -255,7 +255,7 @@ export const settingsMachine = model.createMachine(
|
||||
hasPartialData: (_, event) =>
|
||||
event.response != null && event.response.appId == null,
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export function createSettingsMachine(serviceRefs: AppServices) {
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
import {createModel} from 'xstate/lib/model';
|
||||
import {generateSecureRandom} from 'react-native-securerandom';
|
||||
import {log} from 'xstate/lib/actions';
|
||||
import { MY_VCS_STORE_KEY, VC_ITEM_STORE_KEY_REGEX } from '../shared/constants';
|
||||
import {MY_VCS_STORE_KEY} from '../shared/constants';
|
||||
import SecureKeystore from 'react-native-secure-keystore';
|
||||
import {
|
||||
AUTH_TIMEOUT,
|
||||
@@ -24,8 +24,8 @@ import {
|
||||
HMAC_ALIAS,
|
||||
isCustomSecureKeystore,
|
||||
} from '../shared/cryptoutil/cryptoUtil';
|
||||
import {VCMetadata} from '../shared/VCMetadata';
|
||||
|
||||
const vcKeyRegExp = new RegExp(VC_ITEM_STORE_KEY_REGEX);
|
||||
export const keyinvalidatedString =
|
||||
'Key Invalidated due to biometric enrollment';
|
||||
export const tamperedErrorMessageString = 'Data is tampered';
|
||||
@@ -61,7 +61,7 @@ const model = createModel(
|
||||
TAMPERED_VC: () => ({}),
|
||||
RESET_IS_TAMPERED: () => ({}),
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export const StoreEvents = model.events;
|
||||
@@ -213,7 +213,7 @@ export const storeMachine =
|
||||
(_, event) => model.events.STORE_RESPONSE(event.response),
|
||||
{
|
||||
to: (_, event) => event.requester,
|
||||
}
|
||||
},
|
||||
),
|
||||
sendUpdate(),
|
||||
],
|
||||
@@ -261,7 +261,7 @@ export const storeMachine =
|
||||
...event,
|
||||
requester: meta._event.origin,
|
||||
}),
|
||||
{ to: '_store' }
|
||||
{to: '_store'},
|
||||
),
|
||||
|
||||
setEncryptionKey: model.assign({
|
||||
@@ -271,13 +271,13 @@ export const storeMachine =
|
||||
|
||||
services: {
|
||||
clear,
|
||||
hasAndroidEncryptionKey: () => async (callback) => {
|
||||
hasAndroidEncryptionKey: () => async callback => {
|
||||
const hasSetCredentials = SecureKeystore.hasAlias(ENCRYPTION_ID);
|
||||
if (hasSetCredentials) {
|
||||
try {
|
||||
await SecureKeystore.encryptData(
|
||||
DUMMY_KEY_FOR_BIOMETRIC_ALIAS,
|
||||
'Dummy'
|
||||
'Dummy',
|
||||
);
|
||||
} catch (e) {
|
||||
if (e.message.includes(keyinvalidatedString)) {
|
||||
@@ -292,12 +292,12 @@ export const storeMachine =
|
||||
} else {
|
||||
callback(
|
||||
model.events.ERROR(
|
||||
new Error('Could not get the android Key alias')
|
||||
)
|
||||
new Error('Could not get the android Key alias'),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
checkStorageInitialisedOrNot: () => async (callback) => {
|
||||
checkStorageInitialisedOrNot: () => async callback => {
|
||||
const isDirectoryExist = await Storage.isVCStorageInitialised();
|
||||
if (!isDirectoryExist) {
|
||||
callback(model.events.READY());
|
||||
@@ -305,15 +305,15 @@ export const storeMachine =
|
||||
callback(
|
||||
model.events.ERROR(
|
||||
new Error(
|
||||
'vc directory exists and decryption key is not available'
|
||||
)
|
||||
)
|
||||
'vc directory exists and decryption key is not available',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
store: (context) => (callback, onReceive: Receiver<ForwardedEvent>) => {
|
||||
onReceive(async (event) => {
|
||||
store: context => (callback, onReceive: Receiver<ForwardedEvent>) => {
|
||||
onReceive(async event => {
|
||||
try {
|
||||
let response: unknown;
|
||||
switch (event.type) {
|
||||
@@ -321,7 +321,7 @@ export const storeMachine =
|
||||
response = await getItem(
|
||||
event.key,
|
||||
null,
|
||||
context.encryptionKey
|
||||
context.encryptionKey,
|
||||
);
|
||||
break;
|
||||
}
|
||||
@@ -334,7 +334,7 @@ export const storeMachine =
|
||||
await appendItem(
|
||||
event.key,
|
||||
event.value,
|
||||
context.encryptionKey
|
||||
context.encryptionKey,
|
||||
);
|
||||
response = event.value;
|
||||
break;
|
||||
@@ -343,7 +343,7 @@ export const storeMachine =
|
||||
await prependItem(
|
||||
event.key,
|
||||
event.value,
|
||||
context.encryptionKey
|
||||
context.encryptionKey,
|
||||
);
|
||||
|
||||
response = event.value;
|
||||
@@ -353,7 +353,7 @@ export const storeMachine =
|
||||
await updateItem(
|
||||
event.key,
|
||||
event.value,
|
||||
context.encryptionKey
|
||||
context.encryptionKey,
|
||||
);
|
||||
|
||||
response = event.value;
|
||||
@@ -363,7 +363,7 @@ export const storeMachine =
|
||||
await removeItem(
|
||||
event.key,
|
||||
event.value,
|
||||
context.encryptionKey
|
||||
context.encryptionKey,
|
||||
);
|
||||
response = event.value;
|
||||
break;
|
||||
@@ -372,7 +372,7 @@ export const storeMachine =
|
||||
await removeVCMetaData(
|
||||
event.key,
|
||||
event.value,
|
||||
context.encryptionKey
|
||||
context.encryptionKey,
|
||||
);
|
||||
response = event.value;
|
||||
break;
|
||||
@@ -381,7 +381,7 @@ export const storeMachine =
|
||||
await removeItems(
|
||||
event.key,
|
||||
event.values,
|
||||
context.encryptionKey
|
||||
context.encryptionKey,
|
||||
);
|
||||
response = event.values;
|
||||
break;
|
||||
@@ -414,7 +414,7 @@ export const storeMachine =
|
||||
}
|
||||
});
|
||||
},
|
||||
getEncryptionKey: () => async (callback) => {
|
||||
getEncryptionKey: () => async callback => {
|
||||
const existingCredentials = await Keychain.getGenericPassword();
|
||||
if (existingCredentials) {
|
||||
console.log('Credentials successfully loaded for user');
|
||||
@@ -423,18 +423,18 @@ export const storeMachine =
|
||||
console.log('Credentials failed to load for user');
|
||||
callback(
|
||||
model.events.ERROR(
|
||||
new Error('Could not get keychain credentials.')
|
||||
)
|
||||
new Error('Could not get keychain credentials.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
generateEncryptionKey: () => async (callback) => {
|
||||
generateEncryptionKey: () => async callback => {
|
||||
const randomBytes = await generateSecureRandom(32);
|
||||
const randomBytesString = binaryToBase64(randomBytes);
|
||||
if (!isCustomSecureKeystore()) {
|
||||
const hasSetCredentials = await Keychain.setGenericPassword(
|
||||
ENCRYPTION_ID,
|
||||
randomBytesString
|
||||
randomBytesString,
|
||||
);
|
||||
|
||||
if (hasSetCredentials) {
|
||||
@@ -442,8 +442,8 @@ export const storeMachine =
|
||||
} else {
|
||||
callback(
|
||||
model.events.ERROR(
|
||||
new Error('Could not generate keychain credentials.')
|
||||
)
|
||||
new Error('Could not generate keychain credentials.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@@ -451,13 +451,13 @@ export const storeMachine =
|
||||
await SecureKeystore.generateKey(
|
||||
ENCRYPTION_ID,
|
||||
isBiometricsEnabled,
|
||||
AUTH_TIMEOUT
|
||||
AUTH_TIMEOUT,
|
||||
);
|
||||
SecureKeystore.generateHmacshaKey(HMAC_ALIAS);
|
||||
SecureKeystore.generateKey(
|
||||
DUMMY_KEY_FOR_BIOMETRIC_ALIAS,
|
||||
isBiometricsEnabled,
|
||||
0
|
||||
0,
|
||||
);
|
||||
callback(model.events.KEY_RECEIVED(''));
|
||||
}
|
||||
@@ -467,13 +467,13 @@ export const storeMachine =
|
||||
guards: {
|
||||
isCustomSecureKeystore: () => isCustomSecureKeystore(),
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export async function setItem(
|
||||
key: string,
|
||||
value: unknown,
|
||||
encryptionKey: string
|
||||
encryptionKey: string,
|
||||
) {
|
||||
try {
|
||||
const data = JSON.stringify(value);
|
||||
@@ -488,17 +488,15 @@ export async function setItem(
|
||||
export async function getItem(
|
||||
key: string,
|
||||
defaultValue: unknown,
|
||||
encryptionKey: string
|
||||
encryptionKey: string,
|
||||
) {
|
||||
try {
|
||||
const data = await Storage.getItem(key, encryptionKey);
|
||||
console.log('getting item for ' + key);
|
||||
console.log(data);
|
||||
if (data != null) {
|
||||
const decryptedData = await decryptJson(encryptionKey, data);
|
||||
return JSON.parse(decryptedData);
|
||||
}
|
||||
if (data === null && vcKeyRegExp.exec(key)) {
|
||||
if (data === null && VCMetadata.isVCKey(key)) {
|
||||
await removeItem(key, data, encryptionKey);
|
||||
throw new Error(tamperedErrorMessageString);
|
||||
} else {
|
||||
@@ -520,7 +518,7 @@ export async function getItem(
|
||||
export async function appendItem(
|
||||
key: string,
|
||||
value: unknown,
|
||||
encryptionKey: string
|
||||
encryptionKey: string,
|
||||
) {
|
||||
try {
|
||||
const list = await getItem(key, [], encryptionKey);
|
||||
@@ -534,7 +532,7 @@ export async function appendItem(
|
||||
export async function prependItem(
|
||||
key: string,
|
||||
value: unknown,
|
||||
encryptionKey: string
|
||||
encryptionKey: string,
|
||||
) {
|
||||
try {
|
||||
const list = await getItem(key, [], encryptionKey);
|
||||
@@ -552,20 +550,21 @@ export async function prependItem(
|
||||
export async function updateItem(
|
||||
key: string,
|
||||
value: string,
|
||||
encryptionKey: string
|
||||
encryptionKey: string,
|
||||
) {
|
||||
// Used for updating VC metadata in the list. Prepends the passed vcmetadata in value and sets ispinned of other vc metadata to false
|
||||
try {
|
||||
const list = await getItem(key, [], encryptionKey);
|
||||
const updatedMetaData = VCMetadata.fromVcMetadataString(value);
|
||||
const newList = [
|
||||
value,
|
||||
...list.map((item) => {
|
||||
const vc = item.split(':');
|
||||
if (vc[3] !== value.split(':')[3]) {
|
||||
vc[4] = 'false';
|
||||
return vc.join(':');
|
||||
...list.map(metadataStr => {
|
||||
const metaData = VCMetadata.fromVcMetadataString(metadataStr);
|
||||
if (metaData.getVcKey() !== updatedMetaData.getVcKey()) {
|
||||
return JSON.stringify(metaData);
|
||||
}
|
||||
}),
|
||||
].filter((value) => value != undefined && value !== null);
|
||||
].filter(value => value != undefined && value !== null);
|
||||
|
||||
await setItem(key, newList, encryptionKey);
|
||||
} catch (e) {
|
||||
@@ -577,22 +576,18 @@ export async function updateItem(
|
||||
export async function removeItem(
|
||||
key: string,
|
||||
value: string,
|
||||
encryptionKey: string
|
||||
encryptionKey: string,
|
||||
) {
|
||||
try {
|
||||
if (value === null && vcKeyRegExp.exec(key)) {
|
||||
if (value === null && VCMetadata.isVCKey(key)) {
|
||||
await Storage.removeItem(key);
|
||||
await removeVCMetaData(MY_VCS_STORE_KEY, key, encryptionKey);
|
||||
} else {
|
||||
} else if (key === MY_VCS_STORE_KEY) {
|
||||
const data = await Storage.getItem(key, encryptionKey);
|
||||
const decryptedData = await decryptJson(encryptionKey, data);
|
||||
const list = JSON.parse(decryptedData);
|
||||
const vcKeyArray = value.split(':');
|
||||
const finalVcKeyArray = vcKeyArray.pop();
|
||||
const finalVcKey = vcKeyArray.join(':');
|
||||
//console.log('finalVcKeyArray', finalVcKeyArray);
|
||||
const newList = list.filter((vc: string) => {
|
||||
return !vc.includes(finalVcKey);
|
||||
const list = JSON.parse(decryptedData) as string[];
|
||||
const newList = list.filter((str: string) => {
|
||||
return VCMetadata.fromVcMetadataString(str).getVcKey() !== value;
|
||||
});
|
||||
|
||||
await setItem(key, newList, encryptionKey);
|
||||
@@ -606,15 +601,15 @@ export async function removeItem(
|
||||
|
||||
export async function removeVCMetaData(
|
||||
key: string,
|
||||
value: string,
|
||||
encryptionKey: string
|
||||
vcKey: string,
|
||||
encryptionKey: string,
|
||||
) {
|
||||
try {
|
||||
const data = await Storage.getItem(key, encryptionKey);
|
||||
const decryptedData = await decryptJson(encryptionKey, data);
|
||||
const list = JSON.parse(decryptedData);
|
||||
const newList = list.filter((vc: string) => {
|
||||
return !vc.includes(value);
|
||||
const list = JSON.parse(decryptedData) as string[];
|
||||
const newList = list.filter((str: string) => {
|
||||
return VCMetadata.fromVcMetadataString(str).getVcKey() !== vcKey;
|
||||
});
|
||||
|
||||
await setItem(key, newList, encryptionKey);
|
||||
@@ -627,20 +622,16 @@ export async function removeVCMetaData(
|
||||
export async function removeItems(
|
||||
key: string,
|
||||
values: string[],
|
||||
encryptionKey: string
|
||||
encryptionKey: string,
|
||||
) {
|
||||
try {
|
||||
const data = await Storage.getItem(key, encryptionKey);
|
||||
const decryptedData = await decryptJson(encryptionKey, data);
|
||||
const list = JSON.parse(decryptedData);
|
||||
const newList = list.filter(function (vc: string) {
|
||||
return !values.find(function (vcKey: string) {
|
||||
const vcKeyArray = vcKey.split(':');
|
||||
const finalVcKeyArray = vcKeyArray.pop();
|
||||
const finalVcKey = vcKeyArray.join(':');
|
||||
return vc.includes(finalVcKey);
|
||||
});
|
||||
});
|
||||
const list = JSON.parse(decryptedData) as string[];
|
||||
const newList = list.filter(
|
||||
(str: string) =>
|
||||
!values.includes(VCMetadata.fromVcMetadataString(str).getVcKey()),
|
||||
);
|
||||
|
||||
await setItem(key, newList, encryptionKey);
|
||||
} catch (e) {
|
||||
|
||||
139
machines/vc.ts
139
machines/vc.ts
@@ -1,35 +1,30 @@
|
||||
import { EventFrom, StateFrom } from 'xstate';
|
||||
import { send, sendParent } from 'xstate';
|
||||
import {EventFrom, send, sendParent, StateFrom} from 'xstate';
|
||||
import {createModel} from 'xstate/lib/model';
|
||||
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 {
|
||||
MY_VCS_STORE_KEY,
|
||||
RECEIVED_VCS_STORE_KEY,
|
||||
VC_ITEM_STORE_KEY,
|
||||
isSameVC,
|
||||
} from '../shared/constants';
|
||||
import {MY_VCS_STORE_KEY, RECEIVED_VCS_STORE_KEY} from '../shared/constants';
|
||||
import {parseMetadatas, VCMetadata} from '../shared/VCMetadata';
|
||||
|
||||
const model = createModel(
|
||||
{
|
||||
serviceRefs: {} as AppServices,
|
||||
myVcs: [] as string[],
|
||||
receivedVcs: [] as string[],
|
||||
myVcs: [] as VCMetadata[],
|
||||
receivedVcs: [] as VCMetadata[],
|
||||
vcs: {} as Record<string, VC>,
|
||||
},
|
||||
{
|
||||
events: {
|
||||
VIEW_VC: (vc: VC) => ({vc}),
|
||||
GET_VC_ITEM: (vcKey: string) => ({ vcKey }),
|
||||
GET_VC_ITEM: (vcMetadata: VCMetadata) => ({vcMetadata}),
|
||||
STORE_RESPONSE: (response: unknown) => ({response}),
|
||||
STORE_ERROR: (error: Error) => ({error}),
|
||||
VC_ADDED: (vcKey: string) => ({ vcKey }),
|
||||
REMOVE_VC_FROM_CONTEXT: (vcKey: string) => ({ vcKey }),
|
||||
VC_UPDATED: (vcKey: string) => ({ vcKey }),
|
||||
VC_RECEIVED: (vcKey: string) => ({ vcKey }),
|
||||
VC_ADDED: (vcMetadata: VCMetadata) => ({vcMetadata}),
|
||||
REMOVE_VC_FROM_CONTEXT: (vcMetadata: VCMetadata) => ({vcMetadata}),
|
||||
VC_METADATA_UPDATED: (vcMetadata: VCMetadata) => ({vcMetadata}),
|
||||
VC_RECEIVED: (vcMetadata: VCMetadata) => ({vcMetadata}),
|
||||
VC_DOWNLOADED: (vc: VC) => ({vc}),
|
||||
VC_UPDATE: (vc: VC) => ({vc}),
|
||||
REFRESH_MY_VCS: () => ({}),
|
||||
@@ -37,7 +32,7 @@ const model = createModel(
|
||||
REFRESH_RECEIVED_VCS: () => ({}),
|
||||
GET_RECEIVED_VCS: () => ({}),
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export const VcEvents = model.events;
|
||||
@@ -145,8 +140,8 @@ export const vcMachine =
|
||||
REMOVE_VC_FROM_CONTEXT: {
|
||||
actions: 'removeVcFromMyVcs',
|
||||
},
|
||||
VC_UPDATED: {
|
||||
actions: ['updateMyVcs', 'setUpdateVc'],
|
||||
VC_METADATA_UPDATED: {
|
||||
actions: ['updateMyVcs', 'setUpdatedVcMetadatas'],
|
||||
},
|
||||
VC_DOWNLOADED: {
|
||||
actions: 'setDownloadedVc',
|
||||
@@ -169,79 +164,80 @@ export const vcMachine =
|
||||
},
|
||||
{
|
||||
actions: {
|
||||
getReceivedVcsResponse: respond((context) => ({
|
||||
getReceivedVcsResponse: respond(context => ({
|
||||
type: 'VC_RESPONSE',
|
||||
response: context.receivedVcs,
|
||||
response: context.receivedVcs || [],
|
||||
})),
|
||||
|
||||
getVcItemResponse: respond((context, event) => {
|
||||
const vc = context.vcs[event.vcKey];
|
||||
const vc = context.vcs[event.vcMetadata?.getVcKey()];
|
||||
return VcItemEvents.GET_VC_RESPONSE(vc);
|
||||
}),
|
||||
|
||||
loadMyVcs: send(StoreEvents.GET(MY_VCS_STORE_KEY), {
|
||||
to: (context) => context.serviceRefs.store,
|
||||
to: context => context.serviceRefs.store,
|
||||
}),
|
||||
|
||||
loadReceivedVcs: send(StoreEvents.GET(RECEIVED_VCS_STORE_KEY), {
|
||||
to: (context) => context.serviceRefs.store,
|
||||
to: context => context.serviceRefs.store,
|
||||
}),
|
||||
|
||||
setMyVcs: model.assign({
|
||||
myVcs: (_context, event) => (event.response || []) as string[],
|
||||
myVcs: (_context, event) => {
|
||||
return parseMetadatas((event.response || []) as object[]);
|
||||
},
|
||||
}),
|
||||
|
||||
setReceivedVcs: model.assign({
|
||||
receivedVcs: (_context, event) => (event.response || []) as string[],
|
||||
receivedVcs: (_context, event) => {
|
||||
return parseMetadatas((event.response || []) as object[]);
|
||||
},
|
||||
}),
|
||||
|
||||
setDownloadedVc: (context, event) => {
|
||||
context.vcs[VC_ITEM_STORE_KEY(event.vc)] = event.vc;
|
||||
const vcUniqueId = VCMetadata.fromVC(event.vc).getVcKey();
|
||||
context.vcs[vcUniqueId] = event.vc;
|
||||
},
|
||||
|
||||
setVcUpdate: (context, event) => {
|
||||
Object.keys(context.vcs).map((vcKey) => {
|
||||
if (isSameVC(vcKey, VC_ITEM_STORE_KEY(event.vc))) {
|
||||
context.vcs[VC_ITEM_STORE_KEY(event.vc)] = context.vcs[vcKey];
|
||||
delete context.vcs[vcKey];
|
||||
return context.vcs[VC_ITEM_STORE_KEY(event.vc)];
|
||||
Object.keys(context.vcs).map(vcUniqueId => {
|
||||
const eventVCMetadata = VCMetadata.fromVC(event.vc);
|
||||
|
||||
if (vcUniqueId === eventVCMetadata.getVcKey()) {
|
||||
context.vcs[eventVCMetadata.getVcKey()] = context.vcs[vcUniqueId];
|
||||
delete context.vcs[vcUniqueId];
|
||||
return context.vcs[eventVCMetadata.getVcKey()];
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
setUpdateVc: send(
|
||||
(_context, event) => {
|
||||
return StoreEvents.UPDATE(MY_VCS_STORE_KEY, event.vcKey);
|
||||
setUpdatedVcMetadatas: send(
|
||||
_context => {
|
||||
return StoreEvents.SET(MY_VCS_STORE_KEY, _context.myVcs);
|
||||
},
|
||||
{ to: (context) => context.serviceRefs.store }
|
||||
{to: context => context.serviceRefs.store},
|
||||
),
|
||||
|
||||
prependToMyVcs: model.assign({
|
||||
myVcs: (context, event) => [event.vcKey, ...context.myVcs],
|
||||
myVcs: (context, event) => [event.vcMetadata, ...context.myVcs],
|
||||
}),
|
||||
|
||||
removeVcFromMyVcs: model.assign({
|
||||
myVcs: (context, event) =>
|
||||
context.myVcs.filter((vc: string) => !vc.includes(event.vcKey)),
|
||||
context.myVcs.filter(
|
||||
(vc: VCMetadata) => !vc.equals(event.vcMetadata),
|
||||
),
|
||||
}),
|
||||
|
||||
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),
|
||||
myVcs: (context, event) => [
|
||||
...getUpdatedVCMetadatas(context.myVcs, event.vcMetadata),
|
||||
],
|
||||
}),
|
||||
|
||||
prependToReceivedVcs: model.assign({
|
||||
receivedVcs: (context, event) => [
|
||||
event.vcKey,
|
||||
event.vcMetadata,
|
||||
...context.receivedVcs,
|
||||
],
|
||||
}),
|
||||
@@ -249,8 +245,10 @@ export const vcMachine =
|
||||
moveExistingVcToTop: model.assign({
|
||||
receivedVcs: (context, event) => {
|
||||
return [
|
||||
event.vcKey,
|
||||
...context.receivedVcs.filter((value) => value !== event.vcKey),
|
||||
event.vcMetadata,
|
||||
...context.receivedVcs.filter(value =>
|
||||
value.equals(event.vcMetadata),
|
||||
),
|
||||
];
|
||||
},
|
||||
}),
|
||||
@@ -258,9 +256,11 @@ export const vcMachine =
|
||||
|
||||
guards: {
|
||||
hasExistingReceivedVc: (context, event) =>
|
||||
context.receivedVcs.includes(event.vcKey),
|
||||
context.receivedVcs.find(vcMetadata =>
|
||||
vcMetadata.equals(event.vcMetadata),
|
||||
) != null,
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export function createVcMachine(serviceRefs: AppServices) {
|
||||
@@ -272,17 +272,17 @@ export function createVcMachine(serviceRefs: AppServices) {
|
||||
|
||||
type State = StateFrom<typeof vcMachine>;
|
||||
|
||||
export function selectMyVcs(state: State) {
|
||||
export function selectMyVcsMetadata(state: State): VCMetadata[] {
|
||||
return state.context.myVcs;
|
||||
}
|
||||
|
||||
export function selectShareableVcs(state: State) {
|
||||
export function selectShareableVcsMetadata(state: State): VCMetadata[] {
|
||||
return state.context.myVcs.filter(
|
||||
(vcKey) => state.context.vcs[vcKey]?.credential != null
|
||||
vcMetadata => state.context.vcs[vcMetadata.getVcKey()]?.credential != null,
|
||||
);
|
||||
}
|
||||
|
||||
export function selectReceivedVcs(state: State) {
|
||||
export function selectReceivedVcsMetadata(state: State): VCMetadata[] {
|
||||
return state.context.receivedVcs;
|
||||
}
|
||||
|
||||
@@ -297,15 +297,34 @@ export function selectIsRefreshingReceivedVcs(state: State) {
|
||||
/*
|
||||
this methods returns all the binded vc's in the wallet.
|
||||
*/
|
||||
export function selectBindedVcs(state: State) {
|
||||
return (state.context.myVcs as Array<string>).filter((key) => {
|
||||
const walletBindingResponse = state.context.vcs[key]?.walletBindingResponse;
|
||||
export function selectBindedVcsMetadata(state: State): VCMetadata[] {
|
||||
return state.context.myVcs.filter(vcMetadata => {
|
||||
const walletBindingResponse =
|
||||
state.context.vcs[vcMetadata.getVcKey()]?.walletBindingResponse;
|
||||
return (
|
||||
!isEmpty(walletBindingResponse) &&
|
||||
!isEmpty(walletBindingResponse?.walletBindingId)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function getUpdatedVCMetadatas(
|
||||
existingVCMetadatas: VCMetadata[],
|
||||
updatedVcMetadata: VCMetadata,
|
||||
) {
|
||||
const isPinStatusUpdated = updatedVcMetadata.isPinned;
|
||||
|
||||
return existingVCMetadatas.map(value => {
|
||||
if (value.equals(updatedVcMetadata)) {
|
||||
return updatedVcMetadata;
|
||||
} else if (isPinStatusUpdated) {
|
||||
return new VCMetadata({...value, isPinned: false});
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function isEmpty(object) {
|
||||
return object == null || object == '' || object == undefined;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import {assign, ErrorPlatformEvent, EventFrom, send, StateFrom} from 'xstate';
|
||||
import {createModel} from 'xstate/lib/model';
|
||||
import {
|
||||
MIMOTO_BASE_URL,
|
||||
MY_VCS_STORE_KEY,
|
||||
VC_ITEM_STORE_KEY,
|
||||
VC_ITEM_STORE_KEY_AFTER_DOWNLOAD,
|
||||
} from '../shared/constants';
|
||||
import {HOST, MIMOTO_BASE_URL, MY_VCS_STORE_KEY} from '../shared/constants';
|
||||
import {AppServices} from '../shared/GlobalContext';
|
||||
import {CredentialDownloadResponse, request} from '../shared/request';
|
||||
import {
|
||||
@@ -34,6 +29,7 @@ import getAllConfigurations, {
|
||||
import {VcEvents} from './vc';
|
||||
import i18n from '../i18n';
|
||||
import SecureKeystore from 'react-native-secure-keystore';
|
||||
import {VCMetadata} from '../shared/VCMetadata';
|
||||
import {
|
||||
sendStartEvent,
|
||||
getData,
|
||||
@@ -47,7 +43,7 @@ const model = createModel(
|
||||
id: '',
|
||||
idType: '' as VcIdType,
|
||||
tag: '',
|
||||
vcKey: '' as string,
|
||||
vcMetadata: {} as VCMetadata,
|
||||
myVcs: [] as string[],
|
||||
generatedOn: null as Date,
|
||||
credential: null as DecodedCredential,
|
||||
@@ -71,7 +67,6 @@ const model = createModel(
|
||||
walletBindingError: '',
|
||||
publicKey: '',
|
||||
privateKey: '',
|
||||
hashedId: '',
|
||||
},
|
||||
{
|
||||
events: {
|
||||
@@ -98,9 +93,9 @@ const model = createModel(
|
||||
PIN_CARD: () => ({}),
|
||||
KEBAB_POPUP: () => ({}),
|
||||
SHOW_ACTIVITY: () => ({}),
|
||||
REMOVE: (vcKey: string) => ({ vcKey }),
|
||||
REMOVE: (vcMetadata: VCMetadata) => ({vcMetadata}),
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export const VcItemEvents = model.events;
|
||||
@@ -207,11 +202,7 @@ export const vcItemMachine =
|
||||
},
|
||||
],
|
||||
CREDENTIAL_DOWNLOADED: {
|
||||
actions: [
|
||||
'setStoreVerifiableCredential',
|
||||
'storeContext',
|
||||
'editVcKey',
|
||||
],
|
||||
actions: ['setStoreVerifiableCredential', 'storeContext'],
|
||||
},
|
||||
STORE_RESPONSE: {
|
||||
actions: [
|
||||
@@ -273,14 +264,11 @@ export const vcItemMachine =
|
||||
},
|
||||
},
|
||||
pinCard: {
|
||||
entry: 'storeContext',
|
||||
on: {
|
||||
STORE_RESPONSE: {
|
||||
actions: ['sendVcUpdated', 'VcUpdated'],
|
||||
entry: 'sendVcUpdated',
|
||||
always: {
|
||||
target: 'idle',
|
||||
},
|
||||
},
|
||||
},
|
||||
kebabPopUp: {
|
||||
on: {
|
||||
DISMISS: {
|
||||
@@ -763,7 +751,7 @@ export const vcItemMachine =
|
||||
},
|
||||
{
|
||||
actions: {
|
||||
setVerifiableCredential: assign((context) => {
|
||||
setVerifiableCredential: assign(context => {
|
||||
return {
|
||||
...context,
|
||||
verifiableCredential: {
|
||||
@@ -785,33 +773,31 @@ export const vcItemMachine =
|
||||
}),
|
||||
|
||||
removeVcMetaDataFromStorage: send(
|
||||
(context) => {
|
||||
const { serviceRefs, ...data } = context;
|
||||
context => {
|
||||
return StoreEvents.REMOVE_VC_METADATA(
|
||||
MY_VCS_STORE_KEY,
|
||||
VC_ITEM_STORE_KEY(context)
|
||||
new VCMetadata(context).getVcKey(),
|
||||
);
|
||||
},
|
||||
{
|
||||
to: (context) => context.serviceRefs.store,
|
||||
}
|
||||
to: context => context.serviceRefs.store,
|
||||
},
|
||||
),
|
||||
|
||||
removeVcMetaDataFromVcMachine: send(
|
||||
(context, _event) => {
|
||||
const { serviceRefs, ...data } = context;
|
||||
context => {
|
||||
return {
|
||||
type: 'REMOVE_VC_FROM_CONTEXT',
|
||||
vcKey: VC_ITEM_STORE_KEY(context),
|
||||
vcMetadata: new VCMetadata(context),
|
||||
};
|
||||
},
|
||||
{
|
||||
to: (context) => context.serviceRefs.vc,
|
||||
}
|
||||
to: context => context.serviceRefs.vc,
|
||||
},
|
||||
),
|
||||
|
||||
setWalletBindingError: assign({
|
||||
walletBindingError: (context, event) =>
|
||||
walletBindingError: () =>
|
||||
i18n.t(`errors.genericError`, {
|
||||
ns: 'common',
|
||||
}),
|
||||
@@ -843,7 +829,7 @@ export const vcItemMachine =
|
||||
event.data as WalletBindingResponse,
|
||||
}),
|
||||
|
||||
setPinCard: assign((context) => {
|
||||
setPinCard: assign(context => {
|
||||
return {
|
||||
...context,
|
||||
isPinned: !context.isPinned,
|
||||
@@ -851,47 +837,46 @@ export const vcItemMachine =
|
||||
}),
|
||||
|
||||
sendVcUpdated: send(
|
||||
(_context, event) =>
|
||||
VcEvents.VC_UPDATED(VC_ITEM_STORE_KEY(event.response) as string),
|
||||
context => VcEvents.VC_METADATA_UPDATED(new VCMetadata(context)),
|
||||
{
|
||||
to: (context) => context.serviceRefs.vc,
|
||||
}
|
||||
to: context => context.serviceRefs.vc,
|
||||
},
|
||||
),
|
||||
|
||||
updateVc: send(
|
||||
(context) => {
|
||||
context => {
|
||||
const {serviceRefs, ...vc} = context;
|
||||
return {type: 'VC_DOWNLOADED', vc};
|
||||
},
|
||||
{
|
||||
to: (context) => context.serviceRefs.vc,
|
||||
}
|
||||
to: context => context.serviceRefs.vc,
|
||||
},
|
||||
),
|
||||
|
||||
VcUpdated: send(
|
||||
(context) => {
|
||||
context => {
|
||||
const {serviceRefs, ...vc} = context;
|
||||
return {type: 'VC_UPDATE', vc};
|
||||
},
|
||||
{
|
||||
to: (context) => context.serviceRefs.vc,
|
||||
}
|
||||
to: context => context.serviceRefs.vc,
|
||||
},
|
||||
),
|
||||
|
||||
setThumbprintForWalletBindingId: send(
|
||||
(context) => {
|
||||
context => {
|
||||
const {walletBindingResponse} = context;
|
||||
const walletBindingIdKey = getBindingCertificateConstant(
|
||||
walletBindingResponse.walletBindingId
|
||||
walletBindingResponse.walletBindingId,
|
||||
);
|
||||
return StoreEvents.SET(
|
||||
walletBindingIdKey,
|
||||
walletBindingResponse.thumbprint
|
||||
walletBindingResponse.thumbprint,
|
||||
);
|
||||
},
|
||||
{
|
||||
to: (context) => context.serviceRefs.store,
|
||||
}
|
||||
to: context => context.serviceRefs.store,
|
||||
},
|
||||
),
|
||||
|
||||
removedVc: send(
|
||||
@@ -899,46 +884,38 @@ export const vcItemMachine =
|
||||
type: 'REFRESH_MY_VCS',
|
||||
}),
|
||||
{
|
||||
to: (context) => context.serviceRefs.vc,
|
||||
}
|
||||
to: context => context.serviceRefs.vc,
|
||||
},
|
||||
),
|
||||
|
||||
requestVcContext: send(
|
||||
(context) => ({
|
||||
context => ({
|
||||
type: 'GET_VC_ITEM',
|
||||
vcKey: VC_ITEM_STORE_KEY(context),
|
||||
vcMetadata: new VCMetadata(context),
|
||||
}),
|
||||
{
|
||||
to: (context) => context.serviceRefs.vc,
|
||||
}
|
||||
to: context => context.serviceRefs.vc,
|
||||
},
|
||||
),
|
||||
|
||||
requestStoredContext: send(
|
||||
(context) => StoreEvents.GET(VC_ITEM_STORE_KEY(context)),
|
||||
context => StoreEvents.GET(new VCMetadata(context).getVcKey()),
|
||||
{
|
||||
to: (context) => context.serviceRefs.store,
|
||||
}
|
||||
to: context => context.serviceRefs.store,
|
||||
},
|
||||
),
|
||||
|
||||
storeContext: send(
|
||||
(context) => {
|
||||
context => {
|
||||
const {serviceRefs, ...data} = context;
|
||||
data.credentialRegistry = MIMOTO_BASE_URL;
|
||||
return StoreEvents.SET(VC_ITEM_STORE_KEY(context), data);
|
||||
return StoreEvents.SET(new VCMetadata(context).getVcKey(), data);
|
||||
},
|
||||
{
|
||||
to: (context) => context.serviceRefs.store,
|
||||
}
|
||||
to: context => context.serviceRefs.store,
|
||||
},
|
||||
),
|
||||
|
||||
editVcKey: send((context) => {
|
||||
const { serviceRefs, ...data } = context;
|
||||
return StoreEvents.SET(
|
||||
VC_ITEM_STORE_KEY_AFTER_DOWNLOAD(context),
|
||||
data
|
||||
);
|
||||
}),
|
||||
|
||||
setTag: model.assign({
|
||||
tag: (_, event) => event.tag,
|
||||
}),
|
||||
@@ -958,11 +935,11 @@ export const vcItemMachine =
|
||||
}),
|
||||
|
||||
storeTag: send(
|
||||
(context) => {
|
||||
context => {
|
||||
const {serviceRefs, ...data} = context;
|
||||
return StoreEvents.SET(VC_ITEM_STORE_KEY(context), data);
|
||||
return StoreEvents.SET(new VCMetadata(context).getVcKey(), data);
|
||||
},
|
||||
{ to: (context) => context.serviceRefs.store }
|
||||
{to: context => context.serviceRefs.store},
|
||||
),
|
||||
|
||||
setCredential: model.assign((context, event) => {
|
||||
@@ -976,10 +953,10 @@ export const vcItemMachine =
|
||||
}),
|
||||
|
||||
logDownloaded: send(
|
||||
(context) => {
|
||||
context => {
|
||||
const {serviceRefs, ...data} = context;
|
||||
return ActivityLogEvents.LOG_ACTIVITY({
|
||||
_vcKey: VC_ITEM_STORE_KEY(data),
|
||||
_vcKey: VCMetadata.fromVC(data).getVcKey(),
|
||||
type: 'VC_DOWNLOADED',
|
||||
timestamp: Date.now(),
|
||||
deviceName: '',
|
||||
@@ -987,65 +964,65 @@ export const vcItemMachine =
|
||||
});
|
||||
},
|
||||
{
|
||||
to: (context) => context.serviceRefs.activityLog,
|
||||
}
|
||||
to: context => context.serviceRefs.activityLog,
|
||||
},
|
||||
),
|
||||
|
||||
logWalletBindingSuccess: send(
|
||||
(context, event) =>
|
||||
context =>
|
||||
ActivityLogEvents.LOG_ACTIVITY({
|
||||
_vcKey: VC_ITEM_STORE_KEY(context),
|
||||
_vcKey: new VCMetadata(context).getVcKey(),
|
||||
type: 'WALLET_BINDING_SUCCESSFULL',
|
||||
timestamp: Date.now(),
|
||||
deviceName: '',
|
||||
vcLabel: context.tag || context.id,
|
||||
}),
|
||||
{
|
||||
to: (context) => context.serviceRefs.activityLog,
|
||||
}
|
||||
to: context => context.serviceRefs.activityLog,
|
||||
},
|
||||
),
|
||||
|
||||
logWalletBindingFailure: send(
|
||||
(context, event) =>
|
||||
context =>
|
||||
ActivityLogEvents.LOG_ACTIVITY({
|
||||
_vcKey: VC_ITEM_STORE_KEY(context),
|
||||
_vcKey: new VCMetadata(context).getVcKey(),
|
||||
type: 'WALLET_BINDING_FAILURE',
|
||||
timestamp: Date.now(),
|
||||
deviceName: '',
|
||||
vcLabel: context.tag || context.id,
|
||||
}),
|
||||
{
|
||||
to: (context) => context.serviceRefs.activityLog,
|
||||
}
|
||||
to: context => context.serviceRefs.activityLog,
|
||||
},
|
||||
),
|
||||
|
||||
logRevoked: send(
|
||||
(context) =>
|
||||
context =>
|
||||
ActivityLogEvents.LOG_ACTIVITY({
|
||||
_vcKey: VC_ITEM_STORE_KEY(context),
|
||||
_vcKey: new VCMetadata(context).getVcKey(),
|
||||
type: 'VC_REVOKED',
|
||||
timestamp: Date.now(),
|
||||
deviceName: '',
|
||||
vcLabel: context.tag || context.id,
|
||||
}),
|
||||
{
|
||||
to: (context) => context.serviceRefs.activityLog,
|
||||
}
|
||||
to: context => context.serviceRefs.activityLog,
|
||||
},
|
||||
),
|
||||
|
||||
revokeVID: send(
|
||||
(context) => {
|
||||
context => {
|
||||
return StoreEvents.REMOVE(
|
||||
MY_VCS_STORE_KEY,
|
||||
VC_ITEM_STORE_KEY(context)
|
||||
new VCMetadata(context).getVcKey(),
|
||||
);
|
||||
},
|
||||
{
|
||||
to: (context) => context.serviceRefs.store,
|
||||
}
|
||||
to: context => context.serviceRefs.store,
|
||||
},
|
||||
),
|
||||
|
||||
markVcValid: assign((context) => {
|
||||
markVcValid: assign(context => {
|
||||
return {
|
||||
...context,
|
||||
isVerified: true,
|
||||
@@ -1064,7 +1041,7 @@ export const vcItemMachine =
|
||||
}),
|
||||
|
||||
setVcKey: model.assign({
|
||||
vcKey: (_, event) => event.vcKey,
|
||||
vcMetadata: (_, event) => event.vcMetadata,
|
||||
}),
|
||||
|
||||
setOtpError: assign({
|
||||
@@ -1075,7 +1052,7 @@ export const vcItemMachine =
|
||||
clearOtp: assign({otp: ''}),
|
||||
|
||||
setLock: assign({
|
||||
locked: (context) => !context.locked,
|
||||
locked: context => !context.locked,
|
||||
}),
|
||||
|
||||
setRevoke: assign({
|
||||
@@ -1083,44 +1060,47 @@ export const vcItemMachine =
|
||||
}),
|
||||
|
||||
storeLock: send(
|
||||
(context) => {
|
||||
context => {
|
||||
const {serviceRefs, ...data} = context;
|
||||
return StoreEvents.SET(VC_ITEM_STORE_KEY(context), data);
|
||||
return StoreEvents.SET(new VCMetadata(context).getVcKey(), data);
|
||||
},
|
||||
{ to: (context) => context.serviceRefs.store }
|
||||
{to: context => context.serviceRefs.store},
|
||||
),
|
||||
|
||||
removeVcItem: send(
|
||||
(_context, event) => {
|
||||
return StoreEvents.REMOVE(MY_VCS_STORE_KEY, _context.vcKey);
|
||||
_context => {
|
||||
return StoreEvents.REMOVE(
|
||||
MY_VCS_STORE_KEY,
|
||||
_context.vcMetadata.getVcKey(),
|
||||
);
|
||||
},
|
||||
{ to: (context) => context.serviceRefs.store }
|
||||
{to: context => context.serviceRefs.store},
|
||||
),
|
||||
|
||||
logVCremoved: send(
|
||||
(context, _) =>
|
||||
ActivityLogEvents.LOG_ACTIVITY({
|
||||
_vcKey: VC_ITEM_STORE_KEY(context),
|
||||
_vcKey: new VCMetadata(context).getVcKey(),
|
||||
type: 'VC_REMOVED',
|
||||
timestamp: Date.now(),
|
||||
deviceName: '',
|
||||
vcLabel: context.id,
|
||||
}),
|
||||
{
|
||||
to: (context) => context.serviceRefs.activityLog,
|
||||
}
|
||||
to: context => context.serviceRefs.activityLog,
|
||||
},
|
||||
),
|
||||
},
|
||||
|
||||
services: {
|
||||
checkDownloadExpiryLimit: async (context) => {
|
||||
checkDownloadExpiryLimit: async context => {
|
||||
var resp = await getAllConfigurations();
|
||||
const maxLimit: number = resp.vcDownloadMaxRetry;
|
||||
const vcDownloadPoolInterval: number = resp.vcDownloadPoolInterval;
|
||||
console.log(maxLimit);
|
||||
if (maxLimit <= context.downloadCounter) {
|
||||
throw new Error(
|
||||
'Download limit expired for request id: ' + context.requestId
|
||||
'Download limit expired for request id: ' + context.requestId,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1132,7 +1112,7 @@ export const vcItemMachine =
|
||||
return downloadProps;
|
||||
},
|
||||
|
||||
addWalletBindnigId: async (context) => {
|
||||
addWalletBindnigId: async context => {
|
||||
const response = await request(
|
||||
'POST',
|
||||
'/residentmobileapp/wallet-binding',
|
||||
@@ -1152,12 +1132,12 @@ export const vcItemMachine =
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
const certificate = response.response.certificate;
|
||||
await savePrivateKey(
|
||||
getBindingCertificateConstant(context.id),
|
||||
certificate
|
||||
certificate,
|
||||
);
|
||||
|
||||
const walletResponse: WalletBindingResponse = {
|
||||
@@ -1169,10 +1149,10 @@ export const vcItemMachine =
|
||||
return walletResponse;
|
||||
},
|
||||
|
||||
updatePrivateKey: async (context) => {
|
||||
updatePrivateKey: async context => {
|
||||
const hasSetPrivateKey: boolean = await savePrivateKey(
|
||||
context.walletBindingResponse.walletBindingId,
|
||||
context.privateKey
|
||||
context.privateKey,
|
||||
);
|
||||
if (!hasSetPrivateKey) {
|
||||
throw new Error('Could not store private key in keystore.');
|
||||
@@ -1180,7 +1160,7 @@ export const vcItemMachine =
|
||||
return '';
|
||||
},
|
||||
|
||||
generateKeyPair: async (context) => {
|
||||
generateKeyPair: async context => {
|
||||
if (!isCustomSecureKeystore()) {
|
||||
return await generateKeys();
|
||||
}
|
||||
@@ -1188,11 +1168,11 @@ export const vcItemMachine =
|
||||
return SecureKeystore.generateKeyPair(
|
||||
context.id,
|
||||
isBiometricsEnabled,
|
||||
0
|
||||
0,
|
||||
);
|
||||
},
|
||||
|
||||
requestBindingOtp: async (context) => {
|
||||
requestBindingOtp: async context => {
|
||||
const response = await request(
|
||||
'POST',
|
||||
'/residentmobileapp/binding-otp',
|
||||
@@ -1202,24 +1182,24 @@ export const vcItemMachine =
|
||||
individualId: context.id,
|
||||
otpChannels: ['EMAIL', 'PHONE'],
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
if (response.response == null) {
|
||||
throw new Error('Could not process request');
|
||||
}
|
||||
},
|
||||
|
||||
checkStatus: (context) => (callback, onReceive) => {
|
||||
checkStatus: context => (callback, onReceive) => {
|
||||
const pollInterval = setInterval(
|
||||
() => callback(model.events.POLL()),
|
||||
context.downloadInterval
|
||||
context.downloadInterval,
|
||||
);
|
||||
|
||||
onReceive(async (event) => {
|
||||
onReceive(async event => {
|
||||
if (event.type === 'POLL_STATUS') {
|
||||
const response = await request(
|
||||
'GET',
|
||||
`/residentmobileapp/credentialshare/request/status/${context.requestId}`
|
||||
`/residentmobileapp/credentialshare/request/status/${context.requestId}`,
|
||||
);
|
||||
switch (response.response?.statusCode) {
|
||||
case 'NEW':
|
||||
@@ -1235,13 +1215,13 @@ export const vcItemMachine =
|
||||
return () => clearInterval(pollInterval);
|
||||
},
|
||||
|
||||
downloadCredential: (context) => (callback, onReceive) => {
|
||||
downloadCredential: context => (callback, onReceive) => {
|
||||
const pollInterval = setInterval(
|
||||
() => callback(model.events.POLL()),
|
||||
context.downloadInterval
|
||||
context.downloadInterval,
|
||||
);
|
||||
|
||||
onReceive(async (event) => {
|
||||
onReceive(async event => {
|
||||
if (event.type === 'POLL_DOWNLOAD') {
|
||||
const response: CredentialDownloadResponse = await request(
|
||||
'POST',
|
||||
@@ -1249,7 +1229,7 @@ export const vcItemMachine =
|
||||
{
|
||||
individualId: context.id,
|
||||
requestId: context.requestId,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
callback(
|
||||
@@ -1267,7 +1247,7 @@ export const vcItemMachine =
|
||||
locked: context.locked,
|
||||
walletBindingResponse: null,
|
||||
credentialRegistry: '',
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1275,11 +1255,11 @@ export const vcItemMachine =
|
||||
return () => clearInterval(pollInterval);
|
||||
},
|
||||
|
||||
verifyCredential: async (context) => {
|
||||
verifyCredential: async context => {
|
||||
return verifyCredential(context.verifiableCredential);
|
||||
},
|
||||
|
||||
requestOtp: async (context) => {
|
||||
requestOtp: async context => {
|
||||
try {
|
||||
return request('POST', '/residentmobileapp/req/otp', {
|
||||
individualId: context.id,
|
||||
@@ -1292,7 +1272,7 @@ export const vcItemMachine =
|
||||
}
|
||||
},
|
||||
|
||||
requestLock: async (context) => {
|
||||
requestLock: async context => {
|
||||
let response = null;
|
||||
if (context.locked) {
|
||||
response = await request(
|
||||
@@ -1305,7 +1285,7 @@ export const vcItemMachine =
|
||||
transactionID: context.transactionId,
|
||||
authType: ['bio'],
|
||||
unlockForSeconds: '120',
|
||||
}
|
||||
},
|
||||
);
|
||||
} else {
|
||||
response = await request(
|
||||
@@ -1317,13 +1297,13 @@ export const vcItemMachine =
|
||||
otp: context.otp,
|
||||
transactionID: context.transactionId,
|
||||
authType: ['bio'],
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
return response.response;
|
||||
},
|
||||
|
||||
requestRevoke: async (context) => {
|
||||
requestRevoke: async context => {
|
||||
try {
|
||||
return request('PATCH', `/residentmobileapp/vid/${context.id}`, {
|
||||
transactionID: context.transactionId,
|
||||
@@ -1346,32 +1326,30 @@ export const vcItemMachine =
|
||||
return vc?.credential != null && vc?.verifiableCredential != null;
|
||||
},
|
||||
|
||||
isDownloadAllowed: (_context, event) => {
|
||||
isDownloadAllowed: _context => {
|
||||
return _context.downloadCounter <= _context.maxDownloadCount;
|
||||
},
|
||||
|
||||
isVcValid: (context) => {
|
||||
isVcValid: context => {
|
||||
return context.isVerified;
|
||||
},
|
||||
|
||||
isCustomSecureKeystore: () => isCustomSecureKeystore(),
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export const createVcItemMachine = (
|
||||
serviceRefs: AppServices,
|
||||
vcKey: string
|
||||
vcMetadata: VCMetadata,
|
||||
) => {
|
||||
const [, idType, hashedId, requestId, isPinned, id] = vcKey.split(':');
|
||||
return vcItemMachine.withContext({
|
||||
...vcItemMachine.context,
|
||||
serviceRefs,
|
||||
id,
|
||||
idType: idType as VcIdType,
|
||||
requestId,
|
||||
isPinned: isPinned == 'true' ? true : false,
|
||||
hashedId,
|
||||
id: vcMetadata.id,
|
||||
idType: vcMetadata.idType as VcIdType,
|
||||
requestId: vcMetadata.requestId,
|
||||
isPinned: vcMetadata.isPinned,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ module.exports = (async () => {
|
||||
assetPlugins: ['expo-asset/tools/hashAssetFiles'],
|
||||
},
|
||||
resolver: {
|
||||
assetExts: assetExts.filter((ext) => ext !== 'svg'),
|
||||
assetExts: assetExts.filter(ext => ext !== 'svg'),
|
||||
sourceExts: [...sourceExts, 'svg'],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -9,14 +9,9 @@ import {
|
||||
} from 'xstate';
|
||||
import {createModel} from 'xstate/lib/model';
|
||||
import {BackendResponseError, request} from '../../../shared/request';
|
||||
import {
|
||||
argon2iConfigForUinVid,
|
||||
argon2iSalt,
|
||||
VC_ITEM_STORE_KEY,
|
||||
} from '../../../shared/constants';
|
||||
import {VcIdType} from '../../../types/vc';
|
||||
import i18n from '../../../i18n';
|
||||
import { hashData } from '../../../shared/commonUtil';
|
||||
import {VCMetadata} from '../../../shared/VCMetadata';
|
||||
|
||||
const model = createModel(
|
||||
{
|
||||
@@ -29,7 +24,6 @@ const model = createModel(
|
||||
transactionId: '',
|
||||
requestId: '',
|
||||
isPinned: false,
|
||||
hashedId: '',
|
||||
},
|
||||
{
|
||||
events: {
|
||||
@@ -41,7 +35,7 @@ const model = createModel(
|
||||
DISMISS: () => ({}),
|
||||
SELECT_ID_TYPE: (idType: VcIdType) => ({idType}),
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export const AddVcModalEvents = model.events;
|
||||
@@ -212,7 +206,7 @@ export const AddVcModalMachine =
|
||||
onDone: [
|
||||
{
|
||||
actions: 'setRequestId',
|
||||
target: 'calculatingHashedId',
|
||||
target: 'done',
|
||||
},
|
||||
],
|
||||
onError: [
|
||||
@@ -228,18 +222,9 @@ export const AddVcModalMachine =
|
||||
],
|
||||
},
|
||||
},
|
||||
calculatingHashedId: {
|
||||
invoke: {
|
||||
src: 'calculateHashedId',
|
||||
onDone: {
|
||||
actions: 'setHashedId',
|
||||
target: 'done',
|
||||
},
|
||||
},
|
||||
},
|
||||
done: {
|
||||
type: 'final',
|
||||
data: (context) => VC_ITEM_STORE_KEY(context),
|
||||
data: context => new VCMetadata(context),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -296,11 +281,6 @@ export const AddVcModalMachine =
|
||||
|
||||
clearId: model.assign({id: ''}),
|
||||
|
||||
setHashedId: model.assign({
|
||||
hashedId: (_context, event) =>
|
||||
(event as DoneInvokeEvent<string>).data,
|
||||
}),
|
||||
|
||||
clearIdError: model.assign({idError: ''}),
|
||||
|
||||
setIdErrorEmpty: model.assign({
|
||||
@@ -337,11 +317,11 @@ export const AddVcModalMachine =
|
||||
|
||||
clearOtp: assign({otp: ''}),
|
||||
|
||||
focusInput: (context) => context.idInputRef.focus(),
|
||||
focusInput: context => context.idInputRef.focus(),
|
||||
},
|
||||
|
||||
services: {
|
||||
requestOtp: async (context) => {
|
||||
requestOtp: async context => {
|
||||
return request('POST', '/residentmobileapp/req/otp', {
|
||||
id: 'mosip.identity.otp.internal',
|
||||
individualId: context.id,
|
||||
@@ -353,9 +333,9 @@ export const AddVcModalMachine =
|
||||
});
|
||||
},
|
||||
|
||||
requestCredential: async (context) => {
|
||||
requestCredential: async context => {
|
||||
// force wait to fix issue with hanging overlay
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
const response = await request(
|
||||
'POST',
|
||||
@@ -365,21 +345,10 @@ export const AddVcModalMachine =
|
||||
individualIdType: context.idType,
|
||||
otp: context.otp,
|
||||
transactionID: context.transactionId,
|
||||
}
|
||||
},
|
||||
);
|
||||
return response.response.requestId;
|
||||
},
|
||||
|
||||
calculateHashedId: async (context) => {
|
||||
const value = context.id;
|
||||
const hashedid = await hashData(
|
||||
value,
|
||||
argon2iSalt,
|
||||
argon2iConfigForUinVid
|
||||
);
|
||||
context.hashedId = hashedid;
|
||||
return hashedid;
|
||||
},
|
||||
},
|
||||
|
||||
guards: {
|
||||
@@ -393,10 +362,10 @@ export const AddVcModalMachine =
|
||||
|
||||
isIdInvalid: (_context, event: unknown) =>
|
||||
['IDA-MLC-009', 'RES-SER-29', 'IDA-MLC-018'].includes(
|
||||
(event as BackendResponseError).name
|
||||
(event as BackendResponseError).name,
|
||||
),
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
type State = StateFrom<typeof AddVcModalMachine>;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
export interface Typegen0 {
|
||||
'@@xstate/typegen': true;
|
||||
'internalEvents': {
|
||||
internalEvents: {
|
||||
'done.invoke.AddVcModal.acceptingIdInput.requestingOtp:invocation[0]': {
|
||||
type: 'done.invoke.AddVcModal.acceptingIdInput.requestingOtp:invocation[0]';
|
||||
data: unknown;
|
||||
@@ -30,19 +30,19 @@ export interface Typegen0 {
|
||||
};
|
||||
'xstate.init': {type: 'xstate.init'};
|
||||
};
|
||||
'invokeSrcNameMap': {
|
||||
invokeSrcNameMap: {
|
||||
requestCredential: 'done.invoke.AddVcModal.requestingCredential:invocation[0]';
|
||||
requestOtp:
|
||||
| 'done.invoke.AddVcModal.acceptingIdInput.requestingOtp:invocation[0]'
|
||||
| 'done.invoke.AddVcModal.acceptingOtpInput.resendOTP:invocation[0]';
|
||||
};
|
||||
'missingImplementations': {
|
||||
missingImplementations: {
|
||||
actions: never;
|
||||
delays: never;
|
||||
guards: never;
|
||||
services: never;
|
||||
};
|
||||
'eventsCausingActions': {
|
||||
eventsCausingActions: {
|
||||
clearId: 'SELECT_ID_TYPE';
|
||||
clearIdError: 'INPUT_ID' | 'SELECT_ID_TYPE' | 'VALIDATE_INPUT';
|
||||
clearOtp:
|
||||
@@ -79,17 +79,17 @@ export interface Typegen0 {
|
||||
| 'error.platform.AddVcModal.requestingCredential:invocation[0]'
|
||||
| 'xstate.init';
|
||||
};
|
||||
'eventsCausingDelays': {};
|
||||
'eventsCausingGuards': {
|
||||
eventsCausingDelays: {};
|
||||
eventsCausingGuards: {
|
||||
isEmptyId: 'VALIDATE_INPUT';
|
||||
isIdInvalid: 'error.platform.AddVcModal.requestingCredential:invocation[0]';
|
||||
isWrongIdFormat: 'VALIDATE_INPUT';
|
||||
};
|
||||
'eventsCausingServices': {
|
||||
eventsCausingServices: {
|
||||
requestCredential: 'INPUT_OTP';
|
||||
requestOtp: 'RESEND_OTP' | 'VALIDATE_INPUT';
|
||||
};
|
||||
'matchesStates':
|
||||
matchesStates:
|
||||
| 'acceptingIdInput'
|
||||
| 'acceptingIdInput.focusing'
|
||||
| 'acceptingIdInput.idle'
|
||||
@@ -114,5 +114,5 @@ export interface Typegen0 {
|
||||
| {invalid?: 'backend' | 'empty' | 'format'};
|
||||
acceptingOtpInput?: 'idle' | 'resendOTP';
|
||||
};
|
||||
'tags': never;
|
||||
tags: never;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import {ActorRefFrom} from 'xstate';
|
||||
import {vcItemMachine} from '../../../machines/vcItem';
|
||||
import {useKebabPopUp} from '../../../components/KebabPopUpController';
|
||||
import {Theme} from '../../../components/ui/styleUtils';
|
||||
import {isSameVC} from '../../../shared/constants';
|
||||
import {VCMetadata} from '../../../shared/VCMetadata';
|
||||
import testIDProps from '../../../shared/commonUtil';
|
||||
|
||||
export const HistoryTab: React.FC<HistoryTabProps> = props => {
|
||||
@@ -28,21 +28,18 @@ export const HistoryTab: React.FC<HistoryTabProps> = props => {
|
||||
</ListItem.Title>
|
||||
</ListItem.Content>
|
||||
<Modal
|
||||
headerLabel={props.vcKey.split(':')[5]}
|
||||
headerLabel={props.vcMetadata.id}
|
||||
isVisible={controller.isShowActivities}
|
||||
onDismiss={controller.DISMISS}>
|
||||
<Column fill>
|
||||
{controller.activities.map(activity => {
|
||||
const vcKeyMatch = isSameVC(activity._vcKey, props.vcKey);
|
||||
if (vcKeyMatch) {
|
||||
return (
|
||||
{controller.activities
|
||||
.filter(activity => activity._vcKey === props.vcMetadata.getVcKey())
|
||||
.map(activity => (
|
||||
<ActivityLogText
|
||||
key={`${activity.timestamp}-${activity._vcKey}`}
|
||||
activity={activity}
|
||||
/>
|
||||
);
|
||||
}
|
||||
})}
|
||||
))}
|
||||
{controller.activities.length === 0 && (
|
||||
<Centered fill>
|
||||
<Icon
|
||||
@@ -69,6 +66,6 @@ export const HistoryTab: React.FC<HistoryTabProps> = props => {
|
||||
export interface HistoryTabProps {
|
||||
testID?: string;
|
||||
label: string;
|
||||
vcKey: string;
|
||||
vcMetadata: VCMetadata;
|
||||
service: ActorRefFrom<typeof vcItemMachine>;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import { GET_INDIVIDUAL_ID } from '../../../shared/constants';
|
||||
import {MessageOverlay} from '../../../components/MessageOverlay';
|
||||
import testIDProps from '../../../shared/commonUtil';
|
||||
|
||||
export const IdInputModal: React.FC<IdInputModalProps> = (props) => {
|
||||
export const IdInputModal: React.FC<IdInputModalProps> = props => {
|
||||
const {t} = useTranslation('IdInputModal');
|
||||
const controller = useIdInputModal(props);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useSelector, useInterpret } from '@xstate/react';
|
||||
import { useContext, useRef, useState } from 'react';
|
||||
import { GlobalContext } from '../../../shared/GlobalContext';
|
||||
import { selectMyVcs, VcEvents } from '../../../machines/vc';
|
||||
import { selectMyVcsMetadata, VcEvents } from '../../../machines/vc';
|
||||
import {
|
||||
createVcItemMachine,
|
||||
isShowingBindingWarning,
|
||||
@@ -24,7 +24,7 @@ export function useWalletBinding(props) {
|
||||
const machine = useRef(
|
||||
createVcItemMachine(
|
||||
appService.getSnapshot().context.serviceRefs,
|
||||
props.vcKey
|
||||
props.vcMetadata
|
||||
)
|
||||
);
|
||||
|
||||
@@ -32,7 +32,7 @@ export function useWalletBinding(props) {
|
||||
|
||||
const vcService = appService.children.get('vc');
|
||||
|
||||
const vcKeys = useSelector(vcService, selectMyVcs);
|
||||
const vcsMetadata = useSelector(vcService, selectMyVcsMetadata);
|
||||
|
||||
const otpError = useSelector(bindingService, selectOtpError);
|
||||
const [isRevoking, setRevoking] = useState(false);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import {Button, Column, Row, Text} from '../../components/ui';
|
||||
import {Theme} from '../../components/ui/styleUtils';
|
||||
import {RefreshControl, Image, View} from 'react-native';
|
||||
import {Image, RefreshControl, View} from 'react-native';
|
||||
import {useMyVcsTab} from './MyVcsTabController';
|
||||
import {HomeScreenTabProps} from './HomeScreen';
|
||||
import {AddVcModal} from './MyVcs/AddVcModal';
|
||||
@@ -14,11 +14,19 @@ import {
|
||||
MessageOverlay,
|
||||
} from '../../components/MessageOverlay';
|
||||
import {Icon} from 'react-native-elements';
|
||||
import {groupBy} from '../../shared/javascript';
|
||||
|
||||
const pinIconProps = {iconName: 'pushpin', iconType: 'antdesign'};
|
||||
|
||||
export const MyVcsTab: React.FC<HomeScreenTabProps> = props => {
|
||||
const {t} = useTranslation('MyVcsTab');
|
||||
const controller = useMyVcsTab(props);
|
||||
const storeErrorTranslationPath = 'errors.savingFailed';
|
||||
const [pinned, unpinned] = groupBy(
|
||||
controller.vcMetadatas,
|
||||
vcMetadata => vcMetadata.isPinned,
|
||||
);
|
||||
const vcMetadataOrderedByPinStatus = pinned.concat(unpinned);
|
||||
|
||||
const getId = () => {
|
||||
controller.DISMISS();
|
||||
@@ -64,7 +72,7 @@ export const MyVcsTab: React.FC<HomeScreenTabProps> = props => {
|
||||
<Column fill style={{display: props.isVisible ? 'flex' : 'none'}}>
|
||||
{controller.isRequestSuccessful && <DownloadingVcPopUp />}
|
||||
<Column fill pY={18} pX={15}>
|
||||
{controller.vcKeys.length > 0 && (
|
||||
{vcMetadataOrderedByPinStatus.length > 0 && (
|
||||
<React.Fragment>
|
||||
<Column
|
||||
scroll
|
||||
@@ -76,31 +84,17 @@ export const MyVcsTab: React.FC<HomeScreenTabProps> = props => {
|
||||
onRefresh={controller.REFRESH}
|
||||
/>
|
||||
}>
|
||||
{controller.vcKeys.map((vcKey, index) => {
|
||||
if (vcKey.split(':')[4] === 'true') {
|
||||
{vcMetadataOrderedByPinStatus.map((vcMetadata, index) => {
|
||||
const iconProps = vcMetadata.isPinned ? pinIconProps : {};
|
||||
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}
|
||||
{...iconProps}
|
||||
key={`${vcMetadata.getVcKey()}-${index}`}
|
||||
vcMetadata={vcMetadata}
|
||||
margin="0 2 8 2"
|
||||
onPress={controller.VIEW_VC}
|
||||
/>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</Column>
|
||||
<Button
|
||||
@@ -112,7 +106,7 @@ export const MyVcsTab: React.FC<HomeScreenTabProps> = props => {
|
||||
/>
|
||||
</React.Fragment>
|
||||
)}
|
||||
{controller.vcKeys.length === 0 && (
|
||||
{controller.vcMetadatas.length === 0 && (
|
||||
<React.Fragment>
|
||||
<Column fill style={Theme.Styles.homeScreenContainer}>
|
||||
<Image source={Theme.DigitalIdentityLogo} />
|
||||
|
||||
@@ -4,7 +4,7 @@ import {ActorRefFrom} from 'xstate';
|
||||
import {selectIsTampered} from '../../machines/store';
|
||||
import {
|
||||
selectIsRefreshingMyVcs,
|
||||
selectMyVcs,
|
||||
selectMyVcsMetadata,
|
||||
VcEvents,
|
||||
} from '../../machines/vc';
|
||||
import {
|
||||
@@ -40,7 +40,7 @@ export function useMyVcsTab(props: HomeScreenTabProps) {
|
||||
AddVcModalService: useSelector(service, selectAddVcModal),
|
||||
GetVcModalService: useSelector(service, selectGetVcModal),
|
||||
|
||||
vcKeys: useSelector(vcService, selectMyVcs),
|
||||
vcMetadatas: useSelector(vcService, selectMyVcsMetadata),
|
||||
isTampered: useSelector(storeService, selectIsTampered),
|
||||
|
||||
isRefreshingVcs: useSelector(vcService, selectIsRefreshingMyVcs),
|
||||
|
||||
@@ -15,6 +15,7 @@ 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';
|
||||
|
||||
const model = createModel(
|
||||
{
|
||||
@@ -163,7 +164,7 @@ export const MyVcsTabMachine = model.createMachine(
|
||||
},
|
||||
|
||||
actions: {
|
||||
refreshMyVc: send((_context, event) => VcEvents.REFRESH_MY_VCS(), {
|
||||
refreshMyVc: send(_context => VcEvents.REFRESH_MY_VCS(), {
|
||||
to: context => context.serviceRefs.vc,
|
||||
}),
|
||||
|
||||
@@ -179,14 +180,14 @@ export const MyVcsTabMachine = model.createMachine(
|
||||
(_context, event) => {
|
||||
return StoreEvents.PREPEND(
|
||||
MY_VCS_STORE_KEY,
|
||||
(event as DoneInvokeEvent<string>).data,
|
||||
(event as DoneInvokeEvent<VCMetadata>).data,
|
||||
);
|
||||
},
|
||||
{to: context => context.serviceRefs.store},
|
||||
),
|
||||
|
||||
sendVcAdded: send(
|
||||
(_context, event) => VcEvents.VC_ADDED(event.response as string),
|
||||
(_context, event) => VcEvents.VC_ADDED(event.response as VCMetadata),
|
||||
{
|
||||
to: context => context.serviceRefs.vc,
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, {useEffect} from 'react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {RefreshControl} from 'react-native';
|
||||
import {Icon} from 'react-native-elements';
|
||||
@@ -8,9 +8,9 @@ import { HomeScreenTabProps } from './HomeScreen';
|
||||
import {useReceivedVcsTab} from './ReceivedVcsTabController';
|
||||
import {VcItem} from '../../components/VcItem';
|
||||
|
||||
export const ReceivedVcsTab: React.FC<HomeScreenTabProps> = (props) => {
|
||||
export const ReceivedVcsTab: React.FC<HomeScreenTabProps> = props => {
|
||||
const {t} = useTranslation('ReceivedVcsTab');
|
||||
const controller = useReceivedVcsTab(props);
|
||||
const controller = useReceivedVcsTab();
|
||||
|
||||
return (
|
||||
<Column fill style={{display: props.isVisible ? 'flex' : 'none'}}>
|
||||
@@ -23,16 +23,16 @@ export const ReceivedVcsTab: React.FC<HomeScreenTabProps> = (props) => {
|
||||
onRefresh={controller.REFRESH}
|
||||
/>
|
||||
}>
|
||||
{controller.vcKeys.map((vcKey) => (
|
||||
{controller.receivedVcsMetadata.map(vcMetadata => (
|
||||
<VcItem
|
||||
key={vcKey}
|
||||
vcKey={vcKey}
|
||||
key={vcMetadata.getVcKey()}
|
||||
vcMetadata={vcMetadata}
|
||||
margin="0 2 8 2"
|
||||
onPress={controller.VIEW_VC}
|
||||
activeTab={props.service.id}
|
||||
/>
|
||||
))}
|
||||
{controller.vcKeys.length === 0 && (
|
||||
{controller.receivedVcsMetadata.length === 0 && (
|
||||
<React.Fragment>
|
||||
<Centered fill>
|
||||
<Icon
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ActorRefFrom } from 'xstate';
|
||||
import {
|
||||
VcEvents,
|
||||
selectIsRefreshingReceivedVcs,
|
||||
selectReceivedVcs,
|
||||
selectReceivedVcsMetadata,
|
||||
} from '../../machines/vc';
|
||||
import { vcItemMachine } from '../../machines/vcItem';
|
||||
import { GlobalContext } from '../../shared/GlobalContext';
|
||||
@@ -48,7 +48,7 @@ export function useReceivedVcsTab() {
|
||||
|
||||
return {
|
||||
isVisible,
|
||||
vcKeys: useSelector(vcService, selectReceivedVcs),
|
||||
receivedVcsMetadata: useSelector(vcService, selectReceivedVcsMetadata),
|
||||
|
||||
isRefreshingVcs: useSelector(vcService, selectIsRefreshingReceivedVcs),
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ const model = createModel(
|
||||
STORE_RESPONSE: (response?: unknown) => ({ response }),
|
||||
STORE_ERROR: (error: Error) => ({ error }),
|
||||
ERROR: (error: Error) => ({ error }),
|
||||
GET_RECEIVED_VCS_RESPONSE: (vcKeys: string[]) => ({ vcKeys }),
|
||||
GET_RECEIVED_VCS_RESPONSE: (vcMetadatas: string[]) => ({ vcMetadatas }),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
@@ -42,7 +42,7 @@ const LanguageSetting: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const ProfileScreen: React.FC<MainRouteProps> = (props) => {
|
||||
export const ProfileScreen: React.FC<MainRouteProps> = props => {
|
||||
const {t} = useTranslation('ProfileScreen');
|
||||
|
||||
const controller = useProfileScreen(props);
|
||||
|
||||
@@ -40,7 +40,7 @@ export function useProfileScreen({ navigation }: MainRouteProps) {
|
||||
const errorMsgBio: string = useSelector(bioService, selectError);
|
||||
const unEnrolledNoticeBio: string = useSelector(
|
||||
bioService,
|
||||
selectUnenrolledNotice
|
||||
selectUnenrolledNotice,
|
||||
);
|
||||
const {t} = useTranslation('AuthScreen');
|
||||
|
||||
@@ -101,29 +101,29 @@ export function useProfileScreen({ navigation }: MainRouteProps) {
|
||||
credentialRegistry: useSelector(settingsService, selectCredentialRegistry),
|
||||
credentialRegistryResponse: useSelector(
|
||||
settingsService,
|
||||
selectCredentialRegistryResponse
|
||||
selectCredentialRegistryResponse,
|
||||
),
|
||||
isBiometricUnlockEnabled: useSelector(
|
||||
settingsService,
|
||||
selectBiometricUnlockEnabled
|
||||
selectBiometricUnlockEnabled,
|
||||
),
|
||||
canUseBiometrics: useSelector(authService, selectCanUseBiometrics),
|
||||
useBiometrics,
|
||||
|
||||
UPDATE_NAME: (names) =>
|
||||
UPDATE_NAME: names =>
|
||||
settingsService.send(SettingsEvents.UPDATE_NAME(names[0].value)),
|
||||
|
||||
UPDATE_VC_LABEL: (labels) =>
|
||||
UPDATE_VC_LABEL: labels =>
|
||||
settingsService.send(SettingsEvents.UPDATE_VC_LABEL(labels[0].value)),
|
||||
|
||||
UPDATE_CREDENTIAL_REGISTRY: (items) =>
|
||||
UPDATE_CREDENTIAL_REGISTRY: items =>
|
||||
settingsService.send(SettingsEvents.UPDATE_MIMOTO_HOST(items[0].value)),
|
||||
|
||||
UPDATE_CREDENTIAL_REGISTRY_RESPONSE: (credentialRegistryResponse: string) =>
|
||||
settingsService.send(
|
||||
SettingsEvents.UPDATE_CREDENTIAL_REGISTRY_RESPONSE(
|
||||
credentialRegistryResponse
|
||||
)
|
||||
credentialRegistryResponse,
|
||||
),
|
||||
),
|
||||
|
||||
TOGGLE_BIOMETRIC: (enable: boolean) =>
|
||||
|
||||
@@ -24,25 +24,27 @@ export const MyBindedVcs: React.FC<MyBindedVcsProps> = (props) => {
|
||||
<React.Fragment>
|
||||
<Column fill style={{ display: props.isVisible ? 'flex' : 'none' }}>
|
||||
<Column fill>
|
||||
{controller.vcKeys.length > 0 && (
|
||||
{controller.shareableVcsMetadata.length > 0 && (
|
||||
<>
|
||||
<Column
|
||||
fill
|
||||
backgroundColor={Theme.Colors.lightGreyBackgroundColor}>
|
||||
<Column padding="16 0" scroll>
|
||||
<Column pX={14}>
|
||||
{controller.vcKeys.length > 0 &&
|
||||
controller.vcKeys.map((vcKey, index) => (
|
||||
{controller.shareableVcsMetadata.length > 0 &&
|
||||
controller.shareableVcsMetadata.map(
|
||||
(vcMetadata, index) => (
|
||||
<VcItem
|
||||
key={vcKey}
|
||||
vcKey={vcKey}
|
||||
key={vcMetadata.getVcKey()}
|
||||
vcMetadata={vcMetadata}
|
||||
margin="0 2 8 2"
|
||||
onPress={controller.SELECT_VC_ITEM(index)}
|
||||
showOnlyBindedVc
|
||||
selectable
|
||||
selected={index === controller.selectedIndex}
|
||||
/>
|
||||
))}
|
||||
)
|
||||
)}
|
||||
</Column>
|
||||
</Column>
|
||||
</Column>
|
||||
@@ -64,7 +66,7 @@ export const MyBindedVcs: React.FC<MyBindedVcsProps> = (props) => {
|
||||
</Column>
|
||||
</>
|
||||
)}
|
||||
{controller.vcKeys.length === 0 && (
|
||||
{controller.shareableVcsMetadata.length === 0 && (
|
||||
<React.Fragment>
|
||||
<Centered fill>
|
||||
<Text weight="semibold" margin="0 0 8 0">
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
selectIsSendingAuthenticate,
|
||||
selectEssentialClaims,
|
||||
} from '../../machines/QrLoginMachine';
|
||||
import { selectBindedVcs } from '../../machines/vc';
|
||||
import { selectBindedVcsMetadata } from '../../machines/vc';
|
||||
import { vcItemMachine } from '../../machines/vcItem';
|
||||
import { GlobalContext } from '../../shared/GlobalContext';
|
||||
import { VC } from '../../types/vc';
|
||||
@@ -51,7 +51,7 @@ export function useQrLogin({ service }: QrLoginProps) {
|
||||
SELECT_VC(vcData);
|
||||
},
|
||||
|
||||
vcKeys: useSelector(vcService, selectBindedVcs),
|
||||
shareableVcsMetadata: useSelector(vcService, selectBindedVcsMetadata),
|
||||
selectedVc: useSelector(service, selectSelectedVc),
|
||||
linkTransactionResponse: useSelector(
|
||||
service,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useSelector } from '@xstate/react';
|
||||
import { useContext } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { selectShareableVcs } from '../../machines/vc';
|
||||
import { selectShareableVcsMetadata } from '../../machines/vc';
|
||||
import { GlobalContext } from '../../shared/GlobalContext';
|
||||
import {
|
||||
selectIsLocationDenied,
|
||||
@@ -30,7 +30,10 @@ export function useScanScreen() {
|
||||
const scanService = appService.children.get('scan');
|
||||
const vcService = appService.children.get('vc');
|
||||
|
||||
const shareableVcs = useSelector(vcService, selectShareableVcs);
|
||||
const shareableVcsMetadata = useSelector(
|
||||
vcService,
|
||||
selectShareableVcsMetadata
|
||||
);
|
||||
|
||||
const isLocationDisabled = useSelector(scanService, selectIsLocationDisabled);
|
||||
const isLocationDenied = useSelector(scanService, selectIsLocationDenied);
|
||||
@@ -67,7 +70,7 @@ export function useScanScreen() {
|
||||
|
||||
return {
|
||||
locationError,
|
||||
isEmpty: !shareableVcs.length,
|
||||
isEmpty: !shareableVcsMetadata.length,
|
||||
isBluetoothPermissionDenied,
|
||||
isNearByDevicesPermissionDenied,
|
||||
isLocationDisabled,
|
||||
|
||||
@@ -29,10 +29,10 @@ export const SelectVcOverlay: React.FC<SelectVcOverlayProps> = (props) => {
|
||||
{t('chooseVc')} <Text weight="semibold">{props.receiverName}</Text>
|
||||
</Text>
|
||||
<Column margin="0 0 32 0" scroll>
|
||||
{props.vcKeys.map((vcKey, index) => (
|
||||
{props.vcMetadatas.map((vcMetadata, index) => (
|
||||
<VcItem
|
||||
key={`${vcKey}-${index}`}
|
||||
vcKey={vcKey}
|
||||
key={`${vcMetadata.getVcKey()}-${index}`}
|
||||
vcMetadata={vcMetadata}
|
||||
margin="0 2 8 2"
|
||||
onPress={controller.selectVcItem(index)}
|
||||
selectable
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useState } from 'react';
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import { vcItemMachine } from '../../machines/vcItem';
|
||||
import { VC } from '../../types/vc';
|
||||
import { VCMetadata } from '../../shared/VCMetadata';
|
||||
|
||||
export function useSelectVcOverlay(props: SelectVcOverlayProps) {
|
||||
const [selectedIndex, setSelectedIndex] = useState<number>(null);
|
||||
@@ -34,7 +35,7 @@ export function useSelectVcOverlay(props: SelectVcOverlayProps) {
|
||||
export interface SelectVcOverlayProps {
|
||||
isVisible: boolean;
|
||||
receiverName: string;
|
||||
vcKeys: string[];
|
||||
vcMetadatas: VCMetadata[];
|
||||
onSelect: (vc: VC) => void;
|
||||
onVerifyAndSelect: (vc: VC) => void;
|
||||
onCancel: () => void;
|
||||
|
||||
@@ -19,11 +19,11 @@ export const SendVcScreen: React.FC = () => {
|
||||
const controller = useSendVcScreen();
|
||||
let service;
|
||||
|
||||
if (controller.vcKeys?.length > 0) {
|
||||
if (controller.shareableVcsMetadata?.length > 0) {
|
||||
const firstVCMachine = useRef(
|
||||
createVcItemMachine(
|
||||
appService.getSnapshot().context.serviceRefs,
|
||||
controller.vcKeys[0]
|
||||
controller.shareableVcsMetadata[0]
|
||||
)
|
||||
);
|
||||
|
||||
@@ -78,10 +78,10 @@ export const SendVcScreen: React.FC = () => {
|
||||
</Text>
|
||||
</Column>
|
||||
<Column scroll>
|
||||
{controller.vcKeys.map((vcKey, index) => (
|
||||
{controller.shareableVcsMetadata.map((vcMetadata, index) => (
|
||||
<VcItem
|
||||
key={vcKey}
|
||||
vcKey={vcKey}
|
||||
key={vcMetadata.getVcKey()}
|
||||
vcMetadata={vcMetadata}
|
||||
margin="0 2 8 2"
|
||||
onPress={controller.SELECT_VC_ITEM(index)}
|
||||
selectable
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useSelector } from '@xstate/react';
|
||||
import { useContext, useState } from 'react';
|
||||
import { ActorRefFrom } from 'xstate';
|
||||
import { selectShareableVcs } from '../../machines/vc';
|
||||
import { selectShareableVcsMetadata } from '../../machines/vc';
|
||||
import { vcItemMachine } from '../../machines/vcItem';
|
||||
import { GlobalContext } from '../../shared/GlobalContext';
|
||||
import {
|
||||
@@ -41,7 +41,7 @@ export function useSendVcScreen() {
|
||||
receiverInfo: useSelector(scanService, selectReceiverInfo),
|
||||
reason: useSelector(scanService, selectReason),
|
||||
vcName: useSelector(scanService, selectVcName),
|
||||
vcKeys: useSelector(vcService, selectShareableVcs),
|
||||
shareableVcsMetadata: useSelector(vcService, selectShareableVcsMetadata),
|
||||
selectedVc: useSelector(scanService, selectSelectedVc),
|
||||
|
||||
isSelectingVc: useSelector(scanService, selectIsSelectingVc),
|
||||
|
||||
@@ -12,7 +12,6 @@ import {CopyButton} from '../../components/CopyButton';
|
||||
import testIDProps from '../../shared/commonUtil';
|
||||
import {__InjiVersion, __TuvaliVersion} from '../../shared/GlobalVariables';
|
||||
|
||||
|
||||
export const AboutInji: React.FC<AboutInjiProps> = ({appId}) => {
|
||||
const {t} = useTranslation('AboutInji');
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Theme } from '../../components/ui/styleUtils';
|
||||
import appMetaData from '../../AppMetaData.md';
|
||||
import {__InjiVersion, __TuvaliVersion} from '../../shared/GlobalVariables';
|
||||
|
||||
export const AppMetaData: React.FC<AppMetaDataProps> = (props) => {
|
||||
export const AppMetaData: React.FC<AppMetaDataProps> = props => {
|
||||
const {t} = useTranslation('AppMetaData');
|
||||
const [isViewing, setIsViewing] = useState(false);
|
||||
|
||||
|
||||
@@ -30,16 +30,16 @@ export const ReceivedCardsModal: React.FC<ReceivedCardsProps> = ({
|
||||
onRefresh={controller.REFRESH}
|
||||
/>
|
||||
}>
|
||||
{controller.vcKeys.map(vcKey => (
|
||||
{controller.receivedVcsMetadata.map(vcMetadata => (
|
||||
<VcItem
|
||||
key={vcKey}
|
||||
vcKey={vcKey}
|
||||
key={vcMetadata.getVcKey()}
|
||||
vcMetadata={vcMetadata}
|
||||
margin="0 2 8 2"
|
||||
isSharingVc
|
||||
onPress={controller.VIEW_VC}
|
||||
/>
|
||||
))}
|
||||
{controller.vcKeys.length === 0 && (
|
||||
{controller.receivedVcsMetadata.length === 0 && (
|
||||
<React.Fragment>
|
||||
<Centered fill>
|
||||
<Icon
|
||||
|
||||
@@ -16,7 +16,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import {useRevoke} from './RevokeController';
|
||||
|
||||
// Intentionally hidden using {display:'none'} - Refer mosip/inji/issue#607
|
||||
export const Revoke: React.FC<RevokeScreenProps> = (props) => {
|
||||
export const Revoke: React.FC<RevokeScreenProps> = props => {
|
||||
const controller = useRevoke();
|
||||
const {t} = useTranslation('ProfileScreen');
|
||||
|
||||
@@ -66,7 +66,7 @@ export const Revoke: React.FC<RevokeScreenProps> = (props) => {
|
||||
<Divider />
|
||||
<Row style={Theme.RevokeStyles.rowStyle} fill>
|
||||
<View style={Theme.RevokeStyles.revokeView}>
|
||||
{controller.vidKeys.length > 0 && (
|
||||
{controller.uniqueVidsMetadata.length > 0 && (
|
||||
<Column
|
||||
scroll
|
||||
refreshControl={
|
||||
@@ -75,19 +75,26 @@ export const Revoke: React.FC<RevokeScreenProps> = (props) => {
|
||||
onRefresh={controller.REFRESH}
|
||||
/>
|
||||
}>
|
||||
{controller.vidKeys.map((vcKey, index) => (
|
||||
{controller.uniqueVidsMetadata.map((vcMetadata, index) => {
|
||||
return (
|
||||
<VidItem
|
||||
key={`${vcKey}-${index}`}
|
||||
vcKey={vcKey}
|
||||
key={`${vcMetadata.getVcKey()}-${index}`}
|
||||
vcMetadata={vcMetadata}
|
||||
margin="0 2 8 2"
|
||||
onPress={controller.selectVcItem(index, vcKey)}
|
||||
onPress={controller.selectVcItem(
|
||||
index,
|
||||
vcMetadata.getVcKey(),
|
||||
)}
|
||||
selectable
|
||||
selected={controller.selectedVidKeys.includes(vcKey)}
|
||||
selected={controller.selectedVidUniqueIds.includes(
|
||||
vcMetadata.getVcKey(),
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</Column>
|
||||
)}
|
||||
{controller.vidKeys.length === 0 && (
|
||||
{controller.uniqueVidsMetadata.length === 0 && (
|
||||
<React.Fragment>
|
||||
<Centered fill>
|
||||
<Text weight="semibold" margin="0 0 8 0">
|
||||
@@ -99,7 +106,7 @@ export const Revoke: React.FC<RevokeScreenProps> = (props) => {
|
||||
</View>
|
||||
<Column margin="0 20">
|
||||
<Button
|
||||
disabled={controller.selectedVidKeys.length === 0}
|
||||
disabled={controller.selectedVidUniqueIds.length === 0}
|
||||
title={t('revokeHeader')}
|
||||
onPress={controller.CONFIRM_REVOKE_VC}
|
||||
/>
|
||||
@@ -118,16 +125,21 @@ export const Revoke: React.FC<RevokeScreenProps> = (props) => {
|
||||
</Text>
|
||||
<Text margin="0 0 12 0">
|
||||
{t('revokingVids', {
|
||||
count: controller.selectedVidKeys.length,
|
||||
count: controller.selectedVidUniqueIds.length,
|
||||
})}
|
||||
</Text>
|
||||
{controller.selectedVidKeys.map((vcKey, index) => (
|
||||
{controller.selectedVidUniqueIds.map((uniqueId, index) => (
|
||||
<View style={Theme.RevokeStyles.flexRow} key={index}>
|
||||
<Text margin="0 8" weight="bold">
|
||||
{'\u2022'}
|
||||
</Text>
|
||||
<Text margin="0 0 0 0" weight="bold">
|
||||
{vcKey.split(':')[2]}
|
||||
{/*TODO: Change this to UIN? and Optimize*/}
|
||||
{
|
||||
controller.uniqueVidsMetadata.find(
|
||||
metadata => metadata.getVcKey() === uniqueId,
|
||||
)?.id
|
||||
}
|
||||
</Text>
|
||||
</View>
|
||||
))}
|
||||
|
||||
@@ -4,7 +4,7 @@ import NetInfo from '@react-native-community/netinfo';
|
||||
import { GlobalContext } from '../../shared/GlobalContext';
|
||||
import {
|
||||
selectIsRefreshingMyVcs,
|
||||
selectMyVcs,
|
||||
selectMyVcsMetadata,
|
||||
VcEvents,
|
||||
} from '../../machines/vc';
|
||||
import { vcItemMachine } from '../../machines/vcItem';
|
||||
@@ -24,7 +24,7 @@ export function useRevoke() {
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const vcService = appService.children.get('vc');
|
||||
const revokeService = appService.children.get('RevokeVids');
|
||||
const vcKeys = useSelector(vcService, selectMyVcs);
|
||||
const vcsMetadata = useSelector(vcService, selectMyVcsMetadata);
|
||||
const isRevokingVc = useSelector(revokeService, selectIsRevokingVc);
|
||||
const isLoggingRevoke = useSelector(revokeService, selectIsLoggingRevoke);
|
||||
const isAcceptingOtpInput = useSelector(
|
||||
@@ -38,20 +38,23 @@ export function useRevoke() {
|
||||
const [toastVisible, setToastVisible] = useState(false);
|
||||
const [message, setMessage] = useState('');
|
||||
const [selectedIndex, setSelectedIndex] = useState<number>(null);
|
||||
const [selectedVidKeys, setSelectedVidKeys] = useState<string[]>([]);
|
||||
const [selectedVidUniqueIds, setSelectedVidUniqueIds] = useState<string[]>(
|
||||
[]
|
||||
);
|
||||
|
||||
const vidKeys = vcKeys.filter((vc) => {
|
||||
const vcKey = vc.split(':');
|
||||
return vcKey[1] === 'VID';
|
||||
});
|
||||
const vidsMetadata = vcsMetadata.filter(
|
||||
(vcMetadata) => vcMetadata.idType === 'VID'
|
||||
);
|
||||
|
||||
const selectVcItem = (index: number, vcKey: string) => {
|
||||
const selectVcItem = (index: number, vcUniqueId: string) => {
|
||||
return () => {
|
||||
setSelectedIndex(index);
|
||||
if (selectedVidKeys.includes(vcKey)) {
|
||||
setSelectedVidKeys(selectedVidKeys.filter((item) => item !== vcKey));
|
||||
if (selectedVidUniqueIds.includes(vcUniqueId)) {
|
||||
setSelectedVidUniqueIds(
|
||||
selectedVidUniqueIds.filter((item) => item !== vcUniqueId)
|
||||
);
|
||||
} else {
|
||||
setSelectedVidKeys((prevArray) => [...prevArray, vcKey]);
|
||||
setSelectedVidUniqueIds((prevArray) => [...prevArray, vcUniqueId]);
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -67,7 +70,7 @@ export function useRevoke() {
|
||||
|
||||
useEffect(() => {
|
||||
if (isRevokingVc) {
|
||||
setSelectedVidKeys([]);
|
||||
setSelectedVidUniqueIds([]);
|
||||
showToast(t('revokeSuccessful'));
|
||||
}
|
||||
if (isLoggingRevoke) {
|
||||
@@ -85,10 +88,10 @@ export function useRevoke() {
|
||||
isViewing,
|
||||
message,
|
||||
selectedIndex,
|
||||
selectedVidKeys,
|
||||
selectedVidUniqueIds,
|
||||
toastVisible,
|
||||
vidKeys: vidKeys.filter(
|
||||
(vcKey, index, vid) => vid.indexOf(vcKey) === index
|
||||
uniqueVidsMetadata: vidsMetadata.filter(
|
||||
(vcMetadata, index, vid) => vid.indexOf(vcMetadata) === index
|
||||
),
|
||||
|
||||
CONFIRM_REVOKE_VC: () => {
|
||||
@@ -101,7 +104,7 @@ export function useRevoke() {
|
||||
revokeService.send(RevokeVidsEvents.INPUT_OTP(otp)),
|
||||
REFRESH: () => vcService.send(VcEvents.REFRESH_MY_VCS()),
|
||||
REVOKE_VC: () => {
|
||||
revokeService.send(RevokeVidsEvents.REVOKE_VCS(selectedVidKeys));
|
||||
revokeService.send(RevokeVidsEvents.REVOKE_VCS(selectedVidUniqueIds));
|
||||
setRevoking(false);
|
||||
//since nested modals/overlays don't work in ios, we need to toggle revoke screen
|
||||
setIsViewing(false);
|
||||
|
||||
@@ -54,7 +54,7 @@ export const SettingScreen: React.FC<
|
||||
const {t} = useTranslation('SettingScreen');
|
||||
const controller = useSettingsScreen(props);
|
||||
|
||||
const updateRegistry = (items) => {
|
||||
const updateRegistry = items => {
|
||||
controller.UPDATE_CREDENTIAL_REGISTRY(items[0].value, items[1].value);
|
||||
};
|
||||
|
||||
|
||||
@@ -131,11 +131,11 @@ export function useSettingsScreen(props: RootRouteProps & RequestRouteProps) {
|
||||
|
||||
UPDATE_CREDENTIAL_REGISTRY: (
|
||||
credentialRegistry: string,
|
||||
esignetHostUrl: string
|
||||
esignetHostUrl: string,
|
||||
) => {
|
||||
settingsService.send(SettingsEvents.UPDATE_ESIGNET_HOST(esignetHostUrl)),
|
||||
settingsService.send(
|
||||
SettingsEvents.UPDATE_MIMOTO_HOST(credentialRegistry)
|
||||
SettingsEvents.UPDATE_MIMOTO_HOST(credentialRegistry),
|
||||
);
|
||||
},
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ const dependencies = require('../package-lock.json').dependencies;
|
||||
function getTuvaliPackageDetails() {
|
||||
let packageVersion, packageCommitId;
|
||||
|
||||
Object.keys(dependencies).forEach((dependencyName) => {
|
||||
Object.keys(dependencies).forEach(dependencyName => {
|
||||
const dependencyData = dependencies[dependencyName];
|
||||
|
||||
if (dependencyName == 'react-native-tuvali') {
|
||||
|
||||
55
shared/VCMetadata.ts
Normal file
55
shared/VCMetadata.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import {VC, VcIdType} from '../types/vc';
|
||||
|
||||
const VC_KEY_PREFIX = 'VC';
|
||||
const VC_ITEM_STORE_KEY_REGEX = '^VC_[a-z0-9-]+$';
|
||||
|
||||
export class VCMetadata {
|
||||
idType: VcIdType | string = '';
|
||||
requestId = '';
|
||||
isPinned = false;
|
||||
id: string = '';
|
||||
static vcKeyRegExp = new RegExp(VC_ITEM_STORE_KEY_REGEX);
|
||||
|
||||
constructor({idType = '', requestId = '', isPinned = false, id = ''} = {}) {
|
||||
this.idType = idType;
|
||||
this.requestId = requestId;
|
||||
this.isPinned = isPinned;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
static fromVC(vc: Partial<VC>) {
|
||||
return new VCMetadata({
|
||||
idType: vc.idType,
|
||||
requestId: vc.requestId,
|
||||
isPinned: vc.isPinned || false,
|
||||
id: vc.id,
|
||||
});
|
||||
}
|
||||
|
||||
static fromVcMetadataString(vcMetadataStr: string) {
|
||||
try {
|
||||
return new VCMetadata(JSON.parse(vcMetadataStr));
|
||||
} catch (e) {
|
||||
console.error('Failed to parse VC Metadata', e);
|
||||
return new VCMetadata();
|
||||
}
|
||||
}
|
||||
|
||||
static isVCKey(key: string): boolean {
|
||||
return VCMetadata.vcKeyRegExp.exec(key) != null;
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return `${VC_KEY_PREFIX}_${this.requestId}`;
|
||||
}
|
||||
|
||||
equals(other: VCMetadata): boolean {
|
||||
return this.getVcKey() === other.getVcKey();
|
||||
}
|
||||
}
|
||||
|
||||
export function parseMetadatas(metadataStrings: object[]) {
|
||||
return metadataStrings.map(o => new VCMetadata(o));
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import argon2 from 'react-native-argon2';
|
||||
export const hashData = async (
|
||||
data: string,
|
||||
salt: string,
|
||||
config: Argon2iConfig
|
||||
config: Argon2iConfig,
|
||||
): Promise<string> => {
|
||||
const result = await argon2(data, salt, config);
|
||||
return result.rawHash as string;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {Platform} from 'react-native';
|
||||
import { VC } from '../types/vc';
|
||||
import {
|
||||
MIMOTO_HOST,
|
||||
ESIGNET_HOST,
|
||||
@@ -10,8 +9,8 @@ import { Argon2iConfig } from './commonUtil';
|
||||
export let MIMOTO_BASE_URL = MIMOTO_HOST;
|
||||
export let ESIGNET_BASE_URL = ESIGNET_HOST;
|
||||
|
||||
export const changeCrendetialRegistry = (host) => (MIMOTO_BASE_URL = host);
|
||||
export const changeEsignetUrl = (host) => (ESIGNET_BASE_URL = host);
|
||||
export const changeCrendetialRegistry = host => (MIMOTO_BASE_URL = host);
|
||||
export const changeEsignetUrl = host => (ESIGNET_BASE_URL = host);
|
||||
|
||||
export const MY_VCS_STORE_KEY = 'myVCs';
|
||||
|
||||
@@ -19,23 +18,6 @@ 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.hashedId}:${vc.requestId}:${vc.isPinned}:${vc.id}`;
|
||||
|
||||
export const VC_ITEM_STORE_KEY_AFTER_DOWNLOAD = (vc: Partial<VC>) =>
|
||||
`vc:${vc.idType}:${vc.hashedId}:${vc.requestId}:${vc.isPinned}`;
|
||||
|
||||
//Regex expression to evaluate if the key is for a VC
|
||||
export const VC_ITEM_STORE_KEY_REGEX =
|
||||
'^vc:(UIN|VID):[a-z0-9]+:[a-z0-9-]+:[true|false]+(:[0-9-]+)?$';
|
||||
|
||||
//To compare the vckey with requestId, when the vc is pinned
|
||||
export const isSameVC = (vcKey: string, pinnedVcKey: string) => {
|
||||
const requestId = vcKey.split(':')[3];
|
||||
const pinnedRequestId = pinnedVcKey.split(':')[3];
|
||||
return requestId === pinnedRequestId;
|
||||
};
|
||||
|
||||
export let individualId = '';
|
||||
|
||||
export const GET_INDIVIDUAL_ID = (ind_Id: string) => {
|
||||
|
||||
14
shared/javascript.ts
Normal file
14
shared/javascript.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export function groupBy<T>(array: T[], predicate: (T) => boolean) {
|
||||
const trueElements = [];
|
||||
const falseElements = [];
|
||||
|
||||
array?.forEach((e) => {
|
||||
if (predicate(e)) {
|
||||
trueElements.push(e);
|
||||
} else {
|
||||
falseElements.push(e);
|
||||
}
|
||||
});
|
||||
|
||||
return [trueElements, falseElements];
|
||||
}
|
||||
@@ -13,7 +13,7 @@ export async function request(
|
||||
method: 'GET' | 'POST' | 'PATCH',
|
||||
path: `/${string}`,
|
||||
body?: Record<string, unknown>,
|
||||
host= MIMOTO_BASE_URL
|
||||
host = MIMOTO_BASE_URL,
|
||||
) {
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -36,7 +36,7 @@ export async function request(
|
||||
'The backend API ' +
|
||||
backendUrl +
|
||||
' returned error code 400 with message --> ' +
|
||||
errorMessage
|
||||
errorMessage,
|
||||
);
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
@@ -50,7 +50,7 @@ export async function request(
|
||||
' returned error response --> error code is : ' +
|
||||
errorCode +
|
||||
' error message is : ' +
|
||||
errorMessage
|
||||
errorMessage,
|
||||
);
|
||||
throw new BackendResponseError(errorCode, errorMessage);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {MMKVLoader} from 'react-native-mmkv-storage';
|
||||
import { VC_ITEM_STORE_KEY_REGEX } from './constants';
|
||||
import CryptoJS from 'crypto-js';
|
||||
import {
|
||||
DocumentDirectoryPath,
|
||||
@@ -23,14 +22,14 @@ import {
|
||||
HMAC_ALIAS,
|
||||
isCustomSecureKeystore,
|
||||
} from './cryptoutil/cryptoUtil';
|
||||
import {VCMetadata} from './VCMetadata';
|
||||
|
||||
const MMKV = new MMKVLoader().initialize();
|
||||
const vcKeyRegExp = new RegExp(VC_ITEM_STORE_KEY_REGEX);
|
||||
const vcDirectoryPath = `${DocumentDirectoryPath}/inji/VC`;
|
||||
|
||||
async function generateHmac(
|
||||
encryptionKey: string,
|
||||
data: string
|
||||
data: string,
|
||||
): Promise<string> {
|
||||
if (!isCustomSecureKeystore()) {
|
||||
return CryptoJS.HmacSHA256(encryptionKey, data).toString();
|
||||
@@ -51,10 +50,10 @@ class Storage {
|
||||
static setItem = async (
|
||||
key: string,
|
||||
data: string,
|
||||
encryptionKey?: string
|
||||
encryptionKey?: string,
|
||||
) => {
|
||||
try {
|
||||
const isSavingVC = vcKeyRegExp.exec(key);
|
||||
const isSavingVC = VCMetadata.isVCKey(key);
|
||||
if (isSavingVC) {
|
||||
await this.storeVcHmac(encryptionKey, data, key);
|
||||
return await this.storeVC(key, data);
|
||||
@@ -69,9 +68,9 @@ class Storage {
|
||||
|
||||
static getItem = async (key: string, encryptionKey?: string) => {
|
||||
try {
|
||||
const isSavingVC = vcKeyRegExp.exec(key);
|
||||
const isVCKey = VCMetadata.isVCKey(key);
|
||||
|
||||
if (isSavingVC) {
|
||||
if (isVCKey) {
|
||||
const data = await this.readVCFromFile(key);
|
||||
const isCorrupted = await this.isCorruptedVC(key, encryptionKey, data);
|
||||
|
||||
@@ -88,7 +87,7 @@ class Storage {
|
||||
private static async isCorruptedVC(
|
||||
key: string,
|
||||
encryptionKey: string,
|
||||
data: string
|
||||
data: string,
|
||||
) {
|
||||
const storedHMACofCurrentVC = await this.readHmacForVC(key, encryptionKey);
|
||||
const HMACofVC = await generateHmac(encryptionKey, data);
|
||||
@@ -114,7 +113,7 @@ class Storage {
|
||||
private static async storeVcHmac(
|
||||
encryptionKey: string,
|
||||
data: string,
|
||||
key: string
|
||||
key: string,
|
||||
) {
|
||||
const HMACofVC = await generateHmac(encryptionKey, data);
|
||||
const encryptedHMACofVC = await encryptJson(encryptionKey, HMACofVC);
|
||||
@@ -122,7 +121,7 @@ class Storage {
|
||||
}
|
||||
|
||||
static removeItem = async (key: string) => {
|
||||
if (vcKeyRegExp.exec(key)) {
|
||||
if (VCMetadata.isVCKey(key)) {
|
||||
const path = getFilePath(key);
|
||||
return await unlink(path);
|
||||
}
|
||||
@@ -156,6 +155,7 @@ class Storage {
|
||||
return freeDiskStorageInBytes <= minimumStorageLimitInBytes;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The VC file name will not have the pinned / unpinned state, we will splice the state as this will change.
|
||||
* replace ':' with '_' in the key to get the file name as ':' are not allowed in filenames
|
||||
@@ -171,8 +171,7 @@ const getFileName = (key: string) => {
|
||||
* These paths are coming from DocumentDirectoryPath in react-native-fs.
|
||||
*/
|
||||
const getFilePath = (key: string) => {
|
||||
const fileName = getFileName(key);
|
||||
return `${vcDirectoryPath}/${fileName}.txt`;
|
||||
return `${vcDirectoryPath}/${key}.txt`;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -180,12 +179,12 @@ const getFilePath = (key: string) => {
|
||||
* eg: "vc:UIN:6732935275:e7426576-112f-466a-961a-1ed9635db628:true" is changed to "vc:UIN:6732935275:e7426576-112f-466a-961a-1ed9635db628"
|
||||
*/
|
||||
const getVCKeyName = (key: string) => {
|
||||
return key.split(':').splice(0, 4).join(':');
|
||||
return key;
|
||||
};
|
||||
|
||||
// To print the MMKV data cal this function in getItem
|
||||
const getMMKVData = async () => {
|
||||
const mmkvData = await MMKV.indexer.getKeys();
|
||||
return await MMKV.indexer.getKeys();
|
||||
};
|
||||
|
||||
export default Storage;
|
||||
|
||||
21
types/vc.ts
21
types/vc.ts
@@ -15,9 +15,8 @@ export interface VC {
|
||||
reason?: VCSharingReason[];
|
||||
shouldVerifyPresence?: boolean;
|
||||
walletBindingResponse?: WalletBindingResponse;
|
||||
credentialRegistry: string;
|
||||
credentialRegistry?: string;
|
||||
isPinned?: boolean;
|
||||
hashedId: string;
|
||||
}
|
||||
|
||||
export interface VCSharingReason {
|
||||
@@ -61,25 +60,25 @@ type VCContext = (string | Record<string, unknown>)[];
|
||||
|
||||
export interface VerifiableCredential {
|
||||
'@context': VCContext;
|
||||
'credentialSubject': CredentialSubject;
|
||||
'id': string;
|
||||
'issuanceDate': string;
|
||||
'issuer': string;
|
||||
'proof': {
|
||||
credentialSubject: CredentialSubject;
|
||||
id: string;
|
||||
issuanceDate: string;
|
||||
issuer: string;
|
||||
proof: {
|
||||
created: string;
|
||||
jws: string;
|
||||
proofPurpose: 'assertionMethod' | string;
|
||||
type: 'RsaSignature2018' | string;
|
||||
verificationMethod: string;
|
||||
};
|
||||
'type': VerifiableCredentialType[];
|
||||
type: VerifiableCredentialType[];
|
||||
}
|
||||
|
||||
export interface VerifiablePresentation {
|
||||
'@context': VCContext;
|
||||
'verifiableCredential': VerifiableCredential[];
|
||||
'type': 'VerifiablePresentation';
|
||||
'proof': {
|
||||
verifiableCredential: VerifiableCredential[];
|
||||
type: 'VerifiablePresentation';
|
||||
proof: {
|
||||
created: string;
|
||||
jws: string;
|
||||
proofPurpose: 'authentication' | string;
|
||||
|
||||
Reference in New Issue
Block a user