mirror of
https://github.com/mosip/inji-wallet.git
synced 2026-01-08 21:18:14 -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:
40
App.tsx
40
App.tsx
@@ -1,29 +1,29 @@
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import React, {useContext, useEffect} from 'react';
|
||||
import AppLoading from 'expo-app-loading';
|
||||
import { AppLayout } from './screens/AppLayout';
|
||||
import { useFont } from './shared/hooks/useFont';
|
||||
import { GlobalContextProvider } from './components/GlobalContextProvider';
|
||||
import { GlobalContext } from './shared/GlobalContext';
|
||||
import { useSelector } from '@xstate/react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {AppLayout} from './screens/AppLayout';
|
||||
import {useFont} from './shared/hooks/useFont';
|
||||
import {GlobalContextProvider} from './components/GlobalContextProvider';
|
||||
import {GlobalContext} from './shared/GlobalContext';
|
||||
import {useSelector} from '@xstate/react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {
|
||||
selectIsDecryptError,
|
||||
selectIsKeyInvalidateError,
|
||||
selectIsReadError,
|
||||
selectIsReady,
|
||||
} from './machines/app';
|
||||
import { DualMessageOverlay } from './components/DualMessageOverlay';
|
||||
import { useApp } from './screens/AppController';
|
||||
import { Alert } from 'react-native';
|
||||
import {DualMessageOverlay} from './components/DualMessageOverlay';
|
||||
import {useApp} from './screens/AppController';
|
||||
import {Alert} from 'react-native';
|
||||
import {
|
||||
getAppInfoData,
|
||||
getTelemetryConfigData,
|
||||
initializeTelemetry,
|
||||
sendAppInfoEvent,
|
||||
} from './shared/telemetry/TelemetryUtils';
|
||||
import { ErrorMessageOverlay } from './components/MessageOverlay';
|
||||
import {ErrorMessageOverlay} from './components/MessageOverlay';
|
||||
import SecureKeystore from 'react-native-secure-keystore';
|
||||
import { isCustomSecureKeystore } from './shared/cryptoutil/cryptoUtil';
|
||||
import {isCustomSecureKeystore} from './shared/cryptoutil/cryptoUtil';
|
||||
import i18n from './i18n';
|
||||
|
||||
// kludge: this is a bad practice but has been done temporarily to surface
|
||||
@@ -48,10 +48,10 @@ function configureTelemetry() {
|
||||
}
|
||||
|
||||
const AppLayoutWrapper: React.FC = () => {
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const isDecryptError = useSelector(appService, selectIsDecryptError);
|
||||
const controller = useApp();
|
||||
const { t } = useTranslation('WelcomeScreen');
|
||||
const {t} = useTranslation('WelcomeScreen');
|
||||
if (isDecryptError) {
|
||||
DecryptErrorAlert(controller, t);
|
||||
}
|
||||
@@ -60,14 +60,14 @@ const AppLayoutWrapper: React.FC = () => {
|
||||
};
|
||||
|
||||
const AppLoadingWrapper: React.FC = () => {
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const isReadError = useSelector(appService, selectIsReadError);
|
||||
const isKeyInvalidateError = useSelector(
|
||||
appService,
|
||||
selectIsKeyInvalidateError
|
||||
selectIsKeyInvalidateError,
|
||||
);
|
||||
const controller = useApp();
|
||||
const { t } = useTranslation('WelcomeScreen');
|
||||
const {t} = useTranslation('WelcomeScreen');
|
||||
return (
|
||||
<>
|
||||
<AppLoading />
|
||||
@@ -93,16 +93,16 @@ const AppLoadingWrapper: React.FC = () => {
|
||||
};
|
||||
|
||||
const AppInitialization: React.FC = () => {
|
||||
const { appService } = useContext(GlobalContext);
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const isReady = useSelector(appService, selectIsReady);
|
||||
const hasFontsLoaded = useFont();
|
||||
const { t } = useTranslation('common');
|
||||
const {t} = useTranslation('common');
|
||||
|
||||
useEffect(() => {
|
||||
if (isCustomSecureKeystore()) {
|
||||
SecureKeystore.updatePopup(
|
||||
t('biometricPopup.title'),
|
||||
t('biometricPopup.description')
|
||||
t('biometricPopup.description'),
|
||||
);
|
||||
}
|
||||
}, [i18n.language]);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Dimensions, I18nManager } from 'react-native';
|
||||
import { Icon, ListItem, Overlay, Input } from 'react-native-elements';
|
||||
import { Text, Column, Row, Button } from './ui';
|
||||
import { Theme } from './ui/styleUtils';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {Dimensions, I18nManager} from 'react-native';
|
||||
import {Icon, ListItem, Overlay, Input} from 'react-native-elements';
|
||||
import {Text, Column, Row, Button} from './ui';
|
||||
import {Theme} from './ui/styleUtils';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
|
||||
export const EditableListItem: React.FC<EditableListItemProps> = (props) => {
|
||||
const { t } = useTranslation('common');
|
||||
export const EditableListItem: React.FC<EditableListItemProps> = props => {
|
||||
const {t} = useTranslation('common');
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [items, setItems] = useState(props.items);
|
||||
const [overlayOpened, setOverlayOpened] = useState(true);
|
||||
@@ -18,9 +18,9 @@ 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 };
|
||||
return {...item, value: value};
|
||||
}
|
||||
return item;
|
||||
});
|
||||
@@ -50,18 +50,18 @@ export const EditableListItem: React.FC<EditableListItemProps> = (props) => {
|
||||
color={Theme.Colors.profileLanguageValue}
|
||||
/>
|
||||
<Overlay
|
||||
overlayStyle={{ padding: 24, elevation: 6 }}
|
||||
overlayStyle={{padding: 24, elevation: 6}}
|
||||
isVisible={isEditing}
|
||||
onBackdropPress={dismiss}>
|
||||
<Column width={Dimensions.get('screen').width * 0.8}>
|
||||
{props.items.map((item: ListItemProps, index) => {
|
||||
return (
|
||||
<React.Fragment key={index}>
|
||||
<Text>{t('editLabel', { label: item.label })}</Text>
|
||||
<Text>{t('editLabel', {label: item.label})}</Text>
|
||||
<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());
|
||||
|
||||
@@ -6,7 +6,7 @@ import Storage from '../shared/storage';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import i18next from 'i18next';
|
||||
import RNRestart from 'react-native-restart';
|
||||
import { __SelectedLanguage } from '../shared/GlobalVariables';
|
||||
import {__SelectedLanguage} from '../shared/GlobalVariables';
|
||||
|
||||
export const LanguageSelector: React.FC<LanguageSelectorProps> = props => {
|
||||
const {i18n} = useTranslation();
|
||||
|
||||
@@ -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__});
|
||||
service.subscribe(logState);
|
||||
|
||||
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;
|
||||
|
||||
@@ -3,17 +3,17 @@ import {
|
||||
Button as RNEButton,
|
||||
ButtonProps as RNEButtonProps,
|
||||
} from 'react-native-elements';
|
||||
import { GestureResponderEvent, StyleProp, ViewStyle } from 'react-native';
|
||||
import { Text } from './Text';
|
||||
import { Theme, Spacing } from './styleUtils';
|
||||
import {GestureResponderEvent, StyleProp, ViewStyle} from 'react-native';
|
||||
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,
|
||||
Theme.ButtonStyles[type],
|
||||
{ width: props.width ?? '100%' },
|
||||
{width: props.width ?? '100%'},
|
||||
];
|
||||
const containerStyle: StyleProp<ViewStyle> = [
|
||||
!(type === 'gradient') ? Theme.ButtonStyles.container : null,
|
||||
|
||||
@@ -9,13 +9,13 @@ import {
|
||||
RefreshControlProps,
|
||||
SafeAreaView,
|
||||
} from 'react-native';
|
||||
import { Theme, ElevationLevel, Spacing } from './styleUtils';
|
||||
import {Theme, ElevationLevel, Spacing} from './styleUtils';
|
||||
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,21 +28,21 @@ 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,
|
||||
props.padding ? Theme.spacing('padding', props.padding) : null,
|
||||
props.margin ? Theme.spacing('margin', props.margin) : null,
|
||||
props.backgroundColor ? { backgroundColor: props.backgroundColor } : null,
|
||||
props.width ? { width: props.width } : null,
|
||||
props.height ? { height: props.height } : null,
|
||||
props.align ? { justifyContent: props.align } : null,
|
||||
props.crossAlign ? { alignItems: props.crossAlign } : null,
|
||||
props.backgroundColor ? {backgroundColor: props.backgroundColor} : null,
|
||||
props.width ? {width: props.width} : null,
|
||||
props.height ? {height: props.height} : null,
|
||||
props.align ? {justifyContent: props.align} : null,
|
||||
props.crossAlign ? {alignItems: props.crossAlign} : null,
|
||||
props.elevation ? Theme.elevation(props.elevation) : null,
|
||||
props.style ? props.style : null,
|
||||
props.pY ? { paddingVertical: props.pY } : null,
|
||||
props.pX ? { paddingHorizontal: props.pX } : null,
|
||||
props.pY ? {paddingVertical: props.pY} : null,
|
||||
props.pX ? {paddingHorizontal: props.pX} : null,
|
||||
];
|
||||
|
||||
const ViewType = props.safe ? SafeAreaView : View;
|
||||
@@ -73,7 +73,7 @@ export const Centered = createLayout('column', 'center', 'center');
|
||||
export const HorizontallyCentered = createLayout(
|
||||
'column',
|
||||
'flex-start',
|
||||
'center'
|
||||
'center',
|
||||
);
|
||||
|
||||
interface LayoutProps {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { StyleProp, TextStyle, Text as RNText } from 'react-native';
|
||||
import { Theme, Spacing } from './styleUtils';
|
||||
import {StyleProp, TextStyle, Text as RNText} from 'react-native';
|
||||
import {Theme, Spacing} from './styleUtils';
|
||||
import testIDProps from '../../shared/commonUtil';
|
||||
|
||||
export const Text: React.FC<TextProps> = (props: TextProps) => {
|
||||
@@ -9,8 +9,8 @@ export const Text: React.FC<TextProps> = (props: TextProps) => {
|
||||
const textStyles: StyleProp<TextStyle> = [
|
||||
Theme.TextStyles.base,
|
||||
Theme.TextStyles[weight],
|
||||
props.color ? { color: props.color } : null,
|
||||
props.align ? { textAlign: props.align } : { textAlign: 'left' },
|
||||
props.color ? {color: props.color} : null,
|
||||
props.align ? {textAlign: props.align} : {textAlign: 'left'},
|
||||
props.margin ? Theme.spacing('margin', props.margin) : null,
|
||||
props.size ? Theme.TextStyles[props.size] : null,
|
||||
props.style ? props.style : null,
|
||||
|
||||
16
i18n.ts
16
i18n.ts
@@ -1,6 +1,6 @@
|
||||
import i18next from 'i18next';
|
||||
import * as Localization from 'expo-localization';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import {initReactI18next} from 'react-i18next';
|
||||
|
||||
import en from './locales/en.json';
|
||||
import fil from './locales/fil.json';
|
||||
@@ -10,12 +10,12 @@ import kn from './locales/kan.json';
|
||||
import ta from './locales/tam.json';
|
||||
import Storage from './shared/storage';
|
||||
|
||||
import { iso6393To1 } from 'iso-639-3';
|
||||
import { LocalizedField } from './types/vc';
|
||||
import {iso6393To1} from 'iso-639-3';
|
||||
import {LocalizedField} from './types/vc';
|
||||
|
||||
import { APPLICATION_LANGUAGE } from 'react-native-dotenv';
|
||||
import {APPLICATION_LANGUAGE} from 'react-native-dotenv';
|
||||
|
||||
const resources = { en, fil, ar, hi, kn, ta };
|
||||
const resources = {en, fil, ar, hi, kn, ta};
|
||||
const locale = Localization.locale;
|
||||
const languageCodeMap = {};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -6,29 +6,27 @@ import {
|
||||
sendParent,
|
||||
StateFrom,
|
||||
} from 'xstate';
|
||||
import { createModel } from 'xstate/lib/model';
|
||||
import { AppServices } from '../shared/GlobalContext';
|
||||
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 {createModel} from 'xstate/lib/model';
|
||||
import {AppServices} from '../shared/GlobalContext';
|
||||
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 {
|
||||
getBindingCertificateConstant,
|
||||
getPrivateKey,
|
||||
} from '../shared/keystore/SecureKeystore';
|
||||
import i18n from '../i18n';
|
||||
import { getEndData, sendEndEvent } from '../shared/telemetry/TelemetryUtils';
|
||||
import {parseMetadatas, VCMetadata} from '../shared/VCMetadata';
|
||||
import {getEndData, sendEndEvent} from '../shared/telemetry/TelemetryUtils';
|
||||
|
||||
const model = createModel(
|
||||
{
|
||||
serviceRefs: {} as AppServices,
|
||||
selectedVc: {} as VC,
|
||||
linkCode: '',
|
||||
myVcs: [] as string[],
|
||||
myVcs: [] as VCMetadata[],
|
||||
thumbprint: '',
|
||||
linkTransactionResponse: {} as linkTransactionResponse,
|
||||
authFactors: [],
|
||||
@@ -48,24 +46,24 @@ const model = createModel(
|
||||
},
|
||||
{
|
||||
events: {
|
||||
SELECT_VC: (vc: VC) => ({ vc }),
|
||||
SCANNING_DONE: (params: string) => ({ params }),
|
||||
STORE_RESPONSE: (response: unknown) => ({ response }),
|
||||
STORE_ERROR: (error: Error) => ({ error }),
|
||||
SELECT_VC: (vc: VC) => ({vc}),
|
||||
SCANNING_DONE: (params: string) => ({params}),
|
||||
STORE_RESPONSE: (response: unknown) => ({response}),
|
||||
STORE_ERROR: (error: Error) => ({error}),
|
||||
TOGGLE_CONSENT_CLAIM: (enable: boolean, claim: string) => ({
|
||||
enable,
|
||||
claim,
|
||||
}),
|
||||
DISMISS: () => ({}),
|
||||
CONFIRM: () => ({}),
|
||||
GET: (value: string) => ({ value }),
|
||||
GET: (value: string) => ({value}),
|
||||
VERIFY: () => ({}),
|
||||
CANCEL: () => ({}),
|
||||
FACE_VALID: () => ({}),
|
||||
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) => {
|
||||
@@ -279,7 +279,7 @@ export const qrLoginMachine =
|
||||
|
||||
setSelectedVc: assign({
|
||||
selectedVc: (context, event) => {
|
||||
return { ...event.vc };
|
||||
return {...event.vc};
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -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,10 +331,10 @@ export const qrLoginMachine =
|
||||
} else {
|
||||
context.selectedVoluntaryClaims =
|
||||
context.selectedVoluntaryClaims.filter(
|
||||
(eachClaim) => eachClaim !== event.claim
|
||||
eachClaim => eachClaim !== event.claim,
|
||||
);
|
||||
}
|
||||
return { ...context.isSharing };
|
||||
return {...context.isSharing};
|
||||
},
|
||||
}),
|
||||
setLinkedTransactionId: assign({
|
||||
@@ -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) {
|
||||
|
||||
106
machines/app.ts
106
machines/app.ts
@@ -1,26 +1,26 @@
|
||||
import NetInfo, { NetInfoStateType } from '@react-native-community/netinfo';
|
||||
import { AppState, AppStateStatus, Platform } from 'react-native';
|
||||
import NetInfo, {NetInfoStateType} from '@react-native-community/netinfo';
|
||||
import {AppState, AppStateStatus, Platform} from 'react-native';
|
||||
import {
|
||||
getDeviceId,
|
||||
getDeviceName,
|
||||
getDeviceNameSync,
|
||||
} from 'react-native-device-info';
|
||||
import { EventFrom, spawn, StateFrom, send, assign, AnyState } from 'xstate';
|
||||
import { createModel } from 'xstate/lib/model';
|
||||
import { authMachine, createAuthMachine } from './auth';
|
||||
import { createSettingsMachine, settingsMachine } from './settings';
|
||||
import { StoreEvents, storeMachine } from './store';
|
||||
import { createVcMachine, vcMachine } from './vc';
|
||||
import { createActivityLogMachine, activityLogMachine } from './activityLog';
|
||||
import {EventFrom, spawn, StateFrom, send, assign, AnyState} from 'xstate';
|
||||
import {createModel} from 'xstate/lib/model';
|
||||
import {authMachine, createAuthMachine} from './auth';
|
||||
import {createSettingsMachine, settingsMachine} from './settings';
|
||||
import {StoreEvents, storeMachine} from './store';
|
||||
import {createVcMachine, vcMachine} from './vc';
|
||||
import {createActivityLogMachine, activityLogMachine} from './activityLog';
|
||||
import {
|
||||
createRequestMachine,
|
||||
requestMachine,
|
||||
} from './bleShare/request/requestMachine';
|
||||
import { createScanMachine, scanMachine } from './bleShare/scan/scanMachine';
|
||||
import { createRevokeMachine, revokeVidsMachine } from './revoke';
|
||||
import { pure, respond } from 'xstate/lib/actions';
|
||||
import { AppServices } from '../shared/GlobalContext';
|
||||
import { request } from '../shared/request';
|
||||
import {createScanMachine, scanMachine} from './bleShare/scan/scanMachine';
|
||||
import {createRevokeMachine, revokeVidsMachine} from './revoke';
|
||||
import {pure, respond} from 'xstate/lib/actions';
|
||||
import {AppServices} from '../shared/GlobalContext';
|
||||
import {request} from '../shared/request';
|
||||
import {
|
||||
changeCrendetialRegistry,
|
||||
SETTINGS_STORE_KEY,
|
||||
@@ -47,15 +47,15 @@ const model = createModel(
|
||||
DECRYPT_ERROR_DISMISS: () => ({}),
|
||||
KEY_INVALIDATE_ERROR: () => ({}),
|
||||
OFFLINE: () => ({}),
|
||||
ONLINE: (networkType: NetInfoStateType) => ({ networkType }),
|
||||
ONLINE: (networkType: NetInfoStateType) => ({networkType}),
|
||||
REQUEST_DEVICE_INFO: () => ({}),
|
||||
READY: (data?: unknown) => ({ data }),
|
||||
APP_INFO_RECEIVED: (info: AppInfo) => ({ info }),
|
||||
BACKEND_INFO_RECEIVED: (info: BackendInfo) => ({ info }),
|
||||
STORE_RESPONSE: (response: unknown) => ({ response }),
|
||||
READY: (data?: unknown) => ({data}),
|
||||
APP_INFO_RECEIVED: (info: AppInfo) => ({info}),
|
||||
BACKEND_INFO_RECEIVED: (info: BackendInfo) => ({info}),
|
||||
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,21 +380,21 @@ export const appMachine = model.createMachine(
|
||||
}
|
||||
},
|
||||
|
||||
checkFocusState: () => (callback) => {
|
||||
checkFocusState: () => callback => {
|
||||
const changeHandler = (newState: AppStateStatus) => {
|
||||
switch (newState) {
|
||||
case 'background':
|
||||
case 'inactive':
|
||||
callback({ type: 'INACTIVE' });
|
||||
callback({type: 'INACTIVE'});
|
||||
break;
|
||||
case 'active':
|
||||
callback({ type: 'ACTIVE' });
|
||||
callback({type: 'ACTIVE'});
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const blurHandler = () => callback({ type: 'INACTIVE' });
|
||||
const focusHandler = () => callback({ type: 'ACTIVE' });
|
||||
const blurHandler = () => callback({type: 'INACTIVE'});
|
||||
const focusHandler = () => callback({type: 'ACTIVE'});
|
||||
|
||||
AppState.addEventListener('change', changeHandler);
|
||||
|
||||
@@ -414,17 +414,17 @@ 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 });
|
||||
callback({type: 'ONLINE', networkType: state.type});
|
||||
} else {
|
||||
callback({ type: 'OFFLINE' });
|
||||
callback({type: 'OFFLINE'});
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
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': {
|
||||
'xstate.init': { type: 'xstate.init' };
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { init } from 'mosip-inji-face-sdk';
|
||||
import { assign, ContextFrom, EventFrom, send, StateFrom } from 'xstate';
|
||||
import { createModel } from 'xstate/lib/model';
|
||||
import {init} from 'mosip-inji-face-sdk';
|
||||
import {assign, ContextFrom, EventFrom, send, StateFrom} from 'xstate';
|
||||
import {createModel} from 'xstate/lib/model';
|
||||
import getAllConfigurations, {
|
||||
downloadModel,
|
||||
} from '../shared/commonprops/commonProps';
|
||||
import { AppServices } from '../shared/GlobalContext';
|
||||
import { StoreEvents, StoreResponseEvent } from './store';
|
||||
import { generateSecureRandom } from 'react-native-securerandom';
|
||||
import {AppServices} from '../shared/GlobalContext';
|
||||
import {StoreEvents, StoreResponseEvent} from './store';
|
||||
import {generateSecureRandom} from 'react-native-securerandom';
|
||||
import binaryToBase64 from 'react-native/Libraries/Utilities/binaryToBase64';
|
||||
|
||||
const model = createModel(
|
||||
@@ -20,15 +20,15 @@ const model = createModel(
|
||||
},
|
||||
{
|
||||
events: {
|
||||
SETUP_PASSCODE: (passcode: string) => ({ passcode }),
|
||||
SETUP_BIOMETRICS: (biometrics: string) => ({ biometrics }),
|
||||
SETUP_PASSCODE: (passcode: string) => ({passcode}),
|
||||
SETUP_BIOMETRICS: (biometrics: string) => ({biometrics}),
|
||||
LOGOUT: () => ({}),
|
||||
LOGIN: () => ({}),
|
||||
STORE_RESPONSE: (response?: unknown) => ({ response }),
|
||||
STORE_RESPONSE: (response?: unknown) => ({response}),
|
||||
SELECT: () => ({}),
|
||||
NEXT: () => ({}),
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export const AuthEvents = model.events;
|
||||
@@ -56,7 +56,7 @@ export const authMachine = model.createMachine(
|
||||
target: 'checkingAuth',
|
||||
actions: ['setContext'],
|
||||
},
|
||||
{ target: 'savingDefaults' },
|
||||
{target: 'savingDefaults'},
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -68,10 +68,10 @@ export const authMachine = model.createMachine(
|
||||
},
|
||||
checkingAuth: {
|
||||
always: [
|
||||
{ cond: 'hasLanguageset', target: 'languagesetup' },
|
||||
{ cond: 'hasPasscodeSet', target: 'unauthorized' },
|
||||
{ cond: 'hasBiometricSet', target: 'unauthorized' },
|
||||
{ target: 'settingUp' },
|
||||
{cond: 'hasLanguageset', target: 'languagesetup'},
|
||||
{cond: 'hasPasscodeSet', target: 'unauthorized'},
|
||||
{cond: 'hasBiometricSet', target: 'unauthorized'},
|
||||
{target: 'settingUp'},
|
||||
],
|
||||
},
|
||||
languagesetup: {
|
||||
@@ -131,19 +131,19 @@ export const authMachine = model.createMachine(
|
||||
{
|
||||
actions: {
|
||||
requestStoredContext: send(StoreEvents.GET('auth'), {
|
||||
to: (context) => context.serviceRefs.store,
|
||||
to: context => context.serviceRefs.store,
|
||||
}),
|
||||
|
||||
storeContext: send(
|
||||
(context) => {
|
||||
const { serviceRefs, ...data } = 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) => {
|
||||
const { serviceRefs, ...data } = event.response as ContextFrom<
|
||||
const {serviceRefs, ...data} = event.response as ContextFrom<
|
||||
typeof model
|
||||
>;
|
||||
return data;
|
||||
@@ -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}`, {
|
||||
transactionID: transactionId,
|
||||
vidStatus: 'REVOKED',
|
||||
individualId: vidID,
|
||||
individualIdType: 'VID',
|
||||
otp: context.otp,
|
||||
});
|
||||
return request(
|
||||
'PATCH',
|
||||
`/residentmobileapp/vid/${metadata.id}`,
|
||||
{
|
||||
transactionID: transactionId,
|
||||
vidStatus: 'REVOKED',
|
||||
individualId: metadata.id,
|
||||
individualIdType: 'VID',
|
||||
otp: context.otp,
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
console.log('error.message', error.message);
|
||||
return error;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { assign, ContextFrom, EventFrom, send, StateFrom } from 'xstate';
|
||||
import { createModel } from 'xstate/lib/model';
|
||||
import { AppServices } from '../shared/GlobalContext';
|
||||
import {assign, ContextFrom, EventFrom, send, StateFrom} from 'xstate';
|
||||
import {createModel} from 'xstate/lib/model';
|
||||
import {AppServices} from '../shared/GlobalContext';
|
||||
import {
|
||||
APP_ID_DICTIONARY,
|
||||
APP_ID_LENGTH,
|
||||
@@ -9,15 +9,15 @@ import {
|
||||
SETTINGS_STORE_KEY,
|
||||
ESIGNET_BASE_URL,
|
||||
} from '../shared/constants';
|
||||
import { VCLabel } from '../types/vc';
|
||||
import { StoreEvents } from './store';
|
||||
import {VCLabel} from '../types/vc';
|
||||
import {StoreEvents} from './store';
|
||||
import getAllConfigurations, {
|
||||
COMMON_PROPS_KEY,
|
||||
} from '../shared/commonprops/commonProps';
|
||||
import Storage from '../shared/storage';
|
||||
import ShortUniqueId from 'short-unique-id';
|
||||
import { __AppId } from '../shared/GlobalVariables';
|
||||
import { isCustomSecureKeystore } from '../shared/cryptoutil/cryptoUtil';
|
||||
import {__AppId} from '../shared/GlobalVariables';
|
||||
import {isCustomSecureKeystore} from '../shared/cryptoutil/cryptoUtil';
|
||||
|
||||
const model = createModel(
|
||||
{
|
||||
@@ -36,17 +36,17 @@ const model = createModel(
|
||||
},
|
||||
{
|
||||
events: {
|
||||
UPDATE_NAME: (name: string) => ({ name }),
|
||||
UPDATE_VC_LABEL: (label: string) => ({ label }),
|
||||
TOGGLE_BIOMETRIC_UNLOCK: (enable: boolean) => ({ enable }),
|
||||
STORE_RESPONSE: (response: unknown) => ({ response }),
|
||||
CHANGE_LANGUAGE: (language: string) => ({ language }),
|
||||
UPDATE_NAME: (name: string) => ({name}),
|
||||
UPDATE_VC_LABEL: (label: string) => ({label}),
|
||||
TOGGLE_BIOMETRIC_UNLOCK: (enable: boolean) => ({enable}),
|
||||
STORE_RESPONSE: (response: unknown) => ({response}),
|
||||
CHANGE_LANGUAGE: (language: string) => ({language}),
|
||||
UPDATE_MIMOTO_HOST: (credentialRegistry: string) => ({
|
||||
credentialRegistry,
|
||||
}),
|
||||
UPDATE_ESIGNET_HOST: (esignetHostUrl: string) => ({ esignetHostUrl }),
|
||||
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;
|
||||
@@ -81,8 +81,8 @@ export const settingsMachine = model.createMachine(
|
||||
target: 'idle',
|
||||
actions: ['setContext', 'updatePartialDefaults', 'storeContext'],
|
||||
},
|
||||
{ cond: 'hasData', target: 'idle', actions: ['setContext'] },
|
||||
{ target: 'storingDefaults' },
|
||||
{cond: 'hasData', target: 'idle', actions: ['setContext']},
|
||||
{target: 'storingDefaults'},
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -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) => {
|
||||
const { serviceRefs, ...data } = 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) {
|
||||
|
||||
@@ -9,10 +9,10 @@ import {
|
||||
sendUpdate,
|
||||
StateFrom,
|
||||
} from 'xstate';
|
||||
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 {createModel} from 'xstate/lib/model';
|
||||
import {generateSecureRandom} from 'react-native-securerandom';
|
||||
import {log} from 'xstate/lib/actions';
|
||||
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';
|
||||
@@ -37,38 +37,38 @@ const model = createModel(
|
||||
},
|
||||
{
|
||||
events: {
|
||||
KEY_RECEIVED: (key: string) => ({ key }),
|
||||
KEY_RECEIVED: (key: string) => ({key}),
|
||||
READY: () => ({}),
|
||||
TRY_AGAIN: () => ({}),
|
||||
IGNORE: () => ({}),
|
||||
GET: (key: string) => ({ key }),
|
||||
GET: (key: string) => ({key}),
|
||||
DECRYPT_ERROR: () => ({}),
|
||||
KEY_INVALIDATE_ERROR: () => ({}),
|
||||
SET: (key: string, value: unknown) => ({ key, value }),
|
||||
APPEND: (key: string, value: unknown) => ({ key, value }),
|
||||
PREPEND: (key: string, value: unknown) => ({ key, value }),
|
||||
UPDATE: (key: string, value: string) => ({ key, value }),
|
||||
REMOVE: (key: string, value: string) => ({ key, value }),
|
||||
REMOVE_VC_METADATA: (key: string, value: string) => ({ key, value }),
|
||||
REMOVE_ITEMS: (key: string, values: string[]) => ({ key, values }),
|
||||
SET: (key: string, value: unknown) => ({key, value}),
|
||||
APPEND: (key: string, value: unknown) => ({key, value}),
|
||||
PREPEND: (key: string, value: unknown) => ({key, value}),
|
||||
UPDATE: (key: string, value: string) => ({key, value}),
|
||||
REMOVE: (key: string, value: string) => ({key, value}),
|
||||
REMOVE_VC_METADATA: (key: string, value: string) => ({key, value}),
|
||||
REMOVE_ITEMS: (key: string, values: string[]) => ({key, values}),
|
||||
CLEAR: () => ({}),
|
||||
ERROR: (error: Error) => ({ error }),
|
||||
ERROR: (error: Error) => ({error}),
|
||||
STORE_RESPONSE: (response?: unknown, requester?: string) => ({
|
||||
response,
|
||||
requester,
|
||||
}),
|
||||
STORE_ERROR: (error: Error, requester?: string) => ({ error, requester }),
|
||||
STORE_ERROR: (error: Error, requester?: string) => ({error, requester}),
|
||||
TAMPERED_VC: () => ({}),
|
||||
RESET_IS_TAMPERED: () => ({}),
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export const StoreEvents = model.events;
|
||||
|
||||
export type StoreResponseEvent = EventFrom<typeof model, 'STORE_RESPONSE'>;
|
||||
|
||||
type ForwardedEvent = EventFrom<typeof model> & { requester: string };
|
||||
type ForwardedEvent = EventFrom<typeof model> & {requester: string};
|
||||
|
||||
export const storeMachine =
|
||||
/** @xstate-layout N4IgpgJg5mDOIC5SwC4HsBOYB0MUoEsA7KAUSIGMMBPAB0LSIGkxqBiJ0gTQH0AlUgGFSASQBqpACIBtAAwBdRKFppYBBkSUgAHogBMADj3YArAYDMAdnMBOQ5csA2AIzOTAGhDVErt9kcmzrKGznqyBgYm5gC+0Z6omDh4hCTkVHQaLOykfHwA8nxyikggKmoaWroIhsZmVrb2Tq4eXog2AaY2ACyWdjbBhgbOsfHoWLhgRGAYAIYpZJQ09ASMWRzc-EKiEjIKWmXqK5olVdYG2G4OoeYmlgb9ep7eCDYmjtgWJnp6zjaWvo49CMQAlxjAprN5mklplWGwcvlCnsSgcKidECYuu8vs4uuYAtZHBYDE9EJETKY3OZZLcTJi9OYgXEQWMcFhYGB8MQoABlMYzGBsCCMHDEABuaAA1jhQWy4Jz5nzMAKwAhxWgKHMjkUivtVIdGJUfLI-thZFjMRYrJaSa0Xu1sHo6V0TQFHHiTMDZdh2QruUrZoLphhMNhaAAbOYAM0wAFtsN7fVySAGVWqiBLNRodcjlPq0aAqt1jOFgrJbI47JYfqSEAZrJ0uq9ukMotYvayfWAZhB2ABxUgAFV1KPzRyNCFCJlk-kcljxLibekcjlrQVkM-M+IXRK6PxXHcSXZ77B5Q5HefK4-Rk6dM5X8-xuLsK9r-S6F2sNKaBmCRkP4xYCebAAIIAApgaQAByuzFJeBrHIWPh3rOj6Li+q52r+5wbu0PzmJEhjlgBcrAWBAiQTBF6lGOho3lO95zguz7Lphzz-M4HxYs4kRuE6jJdCRx69mwAgALJ5BI1GoteSG3tOqHMUur52i4FJEno873L8dw9EJQEieJkmkDwYiCDwYlDiBkggYOIHSbRiE6Mh5icdWPzOI4shOG2taBDYlI2PiNiRDc7SesyibdoZpASRIPAiIOsU8g5V50XJDGKU+ylsYge7mNgNwmDY-R3PO4ROvp0XsIIAAypAgUicE0WlTlVJlD5KRhtaMpYFx2E2ZxDBulhVcBPKDgUJkCDyYF5FBZ6pQhE5TsY87LgyFjdO0tZhB+5hdDxJrlnoeGyI4Y0iRNU08AiBRLQWzm3nO-jFS4tgVs4BG1kM2BNq4YRzncDJNrEzJEGgEBwFosp6q1E4ALS5QgSNCck3LQhkRxZHDy03nua7ro6056IdVineWo2RZ24LTFqqSLFjqysLjj1VFixj1hEXldG8XnBG+bz+GVh0hUM-xMqMR5Joq-IwKzslPXYM5dE2+IbtS-Q2G+-zYJYZhfUVNjOJYJqXc88Fs8heKFYb5UmtYhOyJxTYhdWxuVu6EWxEAA */
|
||||
@@ -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) {
|
||||
|
||||
163
machines/vc.ts
163
machines/vc.ts
@@ -1,43 +1,38 @@
|
||||
import { EventFrom, StateFrom } from 'xstate';
|
||||
import { send, sendParent } 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 {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} 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 }),
|
||||
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_DOWNLOADED: (vc: VC) => ({ vc }),
|
||||
VC_UPDATE: (vc: VC) => ({ vc }),
|
||||
VIEW_VC: (vc: VC) => ({vc}),
|
||||
GET_VC_ITEM: (vcMetadata: VCMetadata) => ({vcMetadata}),
|
||||
STORE_RESPONSE: (response: unknown) => ({response}),
|
||||
STORE_ERROR: (error: Error) => ({error}),
|
||||
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: () => ({}),
|
||||
REFRESH_MY_VCS_TWO: (vc: VC) => ({ vc }),
|
||||
REFRESH_MY_VCS_TWO: (vc: VC) => ({vc}),
|
||||
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,29 +1,24 @@
|
||||
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 { AppServices } from '../shared/GlobalContext';
|
||||
import { CredentialDownloadResponse, request } from '../shared/request';
|
||||
import {assign, ErrorPlatformEvent, EventFrom, send, StateFrom} from 'xstate';
|
||||
import {createModel} from 'xstate/lib/model';
|
||||
import {HOST, MIMOTO_BASE_URL, MY_VCS_STORE_KEY} from '../shared/constants';
|
||||
import {AppServices} from '../shared/GlobalContext';
|
||||
import {CredentialDownloadResponse, request} from '../shared/request';
|
||||
import {
|
||||
VC,
|
||||
VerifiableCredential,
|
||||
VcIdType,
|
||||
DecodedCredential,
|
||||
} from '../types/vc';
|
||||
import { StoreEvents } from './store';
|
||||
import { ActivityLogEvents } from './activityLog';
|
||||
import { verifyCredential } from '../shared/vcjs/verifyCredential';
|
||||
import { log } from 'xstate/lib/actions';
|
||||
import {StoreEvents} from './store';
|
||||
import {ActivityLogEvents} from './activityLog';
|
||||
import {verifyCredential} from '../shared/vcjs/verifyCredential';
|
||||
import {log} from 'xstate/lib/actions';
|
||||
import {
|
||||
generateKeys,
|
||||
isCustomSecureKeystore,
|
||||
WalletBindingResponse,
|
||||
} from '../shared/cryptoutil/cryptoUtil';
|
||||
import { KeyPair } from 'react-native-rsa-native';
|
||||
import {KeyPair} from 'react-native-rsa-native';
|
||||
import {
|
||||
getBindingCertificateConstant,
|
||||
savePrivateKey,
|
||||
@@ -31,9 +26,10 @@ import {
|
||||
import getAllConfigurations, {
|
||||
DownloadProps,
|
||||
} from '../shared/commonprops/commonProps';
|
||||
import { VcEvents } from './vc';
|
||||
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,36 +67,35 @@ const model = createModel(
|
||||
walletBindingError: '',
|
||||
publicKey: '',
|
||||
privateKey: '',
|
||||
hashedId: '',
|
||||
},
|
||||
{
|
||||
events: {
|
||||
KEY_RECEIVED: (key: string) => ({ key }),
|
||||
KEY_ERROR: (error: Error) => ({ error }),
|
||||
KEY_RECEIVED: (key: string) => ({key}),
|
||||
KEY_ERROR: (error: Error) => ({error}),
|
||||
EDIT_TAG: () => ({}),
|
||||
SAVE_TAG: (tag: string) => ({ tag }),
|
||||
SAVE_TAG: (tag: string) => ({tag}),
|
||||
STORE_READY: () => ({}),
|
||||
DISMISS: () => ({}),
|
||||
CREDENTIAL_DOWNLOADED: (vc: VC) => ({ vc }),
|
||||
STORE_RESPONSE: (response: VC) => ({ response }),
|
||||
CREDENTIAL_DOWNLOADED: (vc: VC) => ({vc}),
|
||||
STORE_RESPONSE: (response: VC) => ({response}),
|
||||
POLL: () => ({}),
|
||||
DOWNLOAD_READY: () => ({}),
|
||||
GET_VC_RESPONSE: (vc: VC) => ({ vc }),
|
||||
GET_VC_RESPONSE: (vc: VC) => ({vc}),
|
||||
VERIFY: () => ({}),
|
||||
LOCK_VC: () => ({}),
|
||||
INPUT_OTP: (otp: string) => ({ otp }),
|
||||
INPUT_OTP: (otp: string) => ({otp}),
|
||||
REFRESH: () => ({}),
|
||||
REVOKE_VC: () => ({}),
|
||||
ADD_WALLET_BINDING_ID: () => ({}),
|
||||
CANCEL: () => ({}),
|
||||
CONFIRM: () => ({}),
|
||||
STORE_ERROR: (error: Error) => ({ error }),
|
||||
STORE_ERROR: (error: Error) => ({error}),
|
||||
PIN_CARD: () => ({}),
|
||||
KEBAB_POPUP: () => ({}),
|
||||
SHOW_ACTIVITY: () => ({}),
|
||||
REMOVE: (vcKey: string) => ({ vcKey }),
|
||||
REMOVE: (vcMetadata: VCMetadata) => ({vcMetadata}),
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export const VcItemEvents = model.events;
|
||||
@@ -184,7 +179,7 @@ export const vcItemMachine =
|
||||
on: {
|
||||
POLL: {
|
||||
cond: 'isDownloadAllowed',
|
||||
actions: send('POLL_STATUS', { to: 'checkStatus' }),
|
||||
actions: send('POLL_STATUS', {to: 'checkStatus'}),
|
||||
},
|
||||
DOWNLOAD_READY: {
|
||||
target: 'downloadingCredential',
|
||||
@@ -201,17 +196,13 @@ export const vcItemMachine =
|
||||
{
|
||||
cond: 'isDownloadAllowed',
|
||||
actions: [
|
||||
send('POLL_DOWNLOAD', { to: 'downloadCredential' }),
|
||||
send('POLL_DOWNLOAD', {to: 'downloadCredential'}),
|
||||
'incrementDownloadCounter',
|
||||
],
|
||||
},
|
||||
],
|
||||
CREDENTIAL_DOWNLOADED: {
|
||||
actions: [
|
||||
'setStoreVerifiableCredential',
|
||||
'storeContext',
|
||||
'editVcKey',
|
||||
],
|
||||
actions: ['setStoreVerifiableCredential', 'storeContext'],
|
||||
},
|
||||
STORE_RESPONSE: {
|
||||
actions: [
|
||||
@@ -273,12 +264,9 @@ export const vcItemMachine =
|
||||
},
|
||||
},
|
||||
pinCard: {
|
||||
entry: 'storeContext',
|
||||
on: {
|
||||
STORE_RESPONSE: {
|
||||
actions: ['sendVcUpdated', 'VcUpdated'],
|
||||
target: 'idle',
|
||||
},
|
||||
entry: 'sendVcUpdated',
|
||||
always: {
|
||||
target: 'idle',
|
||||
},
|
||||
},
|
||||
kebabPopUp: {
|
||||
@@ -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) => {
|
||||
const { serviceRefs, ...vc } = context;
|
||||
return { type: 'VC_DOWNLOADED', vc };
|
||||
context => {
|
||||
const {serviceRefs, ...vc} = context;
|
||||
return {type: 'VC_DOWNLOADED', vc};
|
||||
},
|
||||
{
|
||||
to: (context) => context.serviceRefs.vc,
|
||||
}
|
||||
to: context => context.serviceRefs.vc,
|
||||
},
|
||||
),
|
||||
|
||||
VcUpdated: send(
|
||||
(context) => {
|
||||
const { serviceRefs, ...vc } = context;
|
||||
return { type: 'VC_UPDATE', vc };
|
||||
context => {
|
||||
const {serviceRefs, ...vc} = context;
|
||||
return {type: 'VC_UPDATE', vc};
|
||||
},
|
||||
{
|
||||
to: (context) => context.serviceRefs.vc,
|
||||
}
|
||||
to: context => context.serviceRefs.vc,
|
||||
},
|
||||
),
|
||||
|
||||
setThumbprintForWalletBindingId: send(
|
||||
(context) => {
|
||||
const { walletBindingResponse } = 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,52 +884,44 @@ 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) => {
|
||||
const { serviceRefs, ...data } = 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,
|
||||
}),
|
||||
|
||||
incrementDownloadCounter: model.assign({
|
||||
downloadCounter: ({ downloadCounter }) => downloadCounter + 1,
|
||||
downloadCounter: ({downloadCounter}) => downloadCounter + 1,
|
||||
}),
|
||||
|
||||
setMaxDownloadCount: model.assign({
|
||||
@@ -958,28 +935,28 @@ export const vcItemMachine =
|
||||
}),
|
||||
|
||||
storeTag: send(
|
||||
(context) => {
|
||||
const { serviceRefs, ...data } = context;
|
||||
return StoreEvents.SET(VC_ITEM_STORE_KEY(context), data);
|
||||
context => {
|
||||
const {serviceRefs, ...data} = context;
|
||||
return StoreEvents.SET(new VCMetadata(context).getVcKey(), data);
|
||||
},
|
||||
{ to: (context) => context.serviceRefs.store }
|
||||
{to: context => context.serviceRefs.store},
|
||||
),
|
||||
|
||||
setCredential: model.assign((context, event) => {
|
||||
switch (event.type) {
|
||||
case 'STORE_RESPONSE':
|
||||
return { ...context, ...event.response };
|
||||
return {...context, ...event.response};
|
||||
case 'GET_VC_RESPONSE':
|
||||
case 'CREDENTIAL_DOWNLOADED':
|
||||
return { ...context, ...event.vc };
|
||||
return {...context, ...event.vc};
|
||||
}
|
||||
}),
|
||||
|
||||
logDownloaded: send(
|
||||
(context) => {
|
||||
const { serviceRefs, ...data } = 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,
|
||||
@@ -1057,14 +1034,14 @@ export const vcItemMachine =
|
||||
transactionId: () => String(new Date().valueOf()).substring(3, 13),
|
||||
}),
|
||||
|
||||
clearTransactionId: assign({ transactionId: '' }),
|
||||
clearTransactionId: assign({transactionId: ''}),
|
||||
|
||||
setOtp: model.assign({
|
||||
otp: (_, event) => event.otp,
|
||||
}),
|
||||
|
||||
setVcKey: model.assign({
|
||||
vcKey: (_, event) => event.vcKey,
|
||||
vcMetadata: (_, event) => event.vcMetadata,
|
||||
}),
|
||||
|
||||
setOtpError: assign({
|
||||
@@ -1072,10 +1049,10 @@ export const vcItemMachine =
|
||||
(event as ErrorPlatformEvent).data.message,
|
||||
}),
|
||||
|
||||
clearOtp: assign({ otp: '' }),
|
||||
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) => {
|
||||
const { serviceRefs, ...data } = context;
|
||||
return StoreEvents.SET(VC_ITEM_STORE_KEY(context), data);
|
||||
context => {
|
||||
const {serviceRefs, ...data} = context;
|
||||
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,39 +1326,37 @@ 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,
|
||||
});
|
||||
};
|
||||
|
||||
type State = StateFrom<typeof vcItemMachine>;
|
||||
|
||||
export function selectVc(state: State) {
|
||||
const { serviceRefs, ...data } = state.context;
|
||||
const {serviceRefs, ...data} = state.context;
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// Learn more https://docs.expo.io/guides/customizing-metro
|
||||
const { getDefaultConfig } = require('expo/metro-config');
|
||||
const {getDefaultConfig} = require('expo/metro-config');
|
||||
|
||||
// extra config is needed to enable `react-native-svg-transformer`
|
||||
module.exports = (async () => {
|
||||
const {
|
||||
resolver: { sourceExts, assetExts },
|
||||
resolver: {sourceExts, assetExts},
|
||||
} = await getDefaultConfig(__dirname);
|
||||
return {
|
||||
transformer: {
|
||||
@@ -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'],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TextInput } from 'react-native';
|
||||
import {TextInput} from 'react-native';
|
||||
import {
|
||||
assign,
|
||||
DoneInvokeEvent,
|
||||
@@ -7,16 +7,11 @@ import {
|
||||
sendParent,
|
||||
StateFrom,
|
||||
} 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 {createModel} from 'xstate/lib/model';
|
||||
import {BackendResponseError, request} from '../../../shared/request';
|
||||
import {VcIdType} from '../../../types/vc';
|
||||
import i18n from '../../../i18n';
|
||||
import { hashData } from '../../../shared/commonUtil';
|
||||
import {VCMetadata} from '../../../shared/VCMetadata';
|
||||
|
||||
const model = createModel(
|
||||
{
|
||||
@@ -29,19 +24,18 @@ const model = createModel(
|
||||
transactionId: '',
|
||||
requestId: '',
|
||||
isPinned: false,
|
||||
hashedId: '',
|
||||
},
|
||||
{
|
||||
events: {
|
||||
INPUT_ID: (id: string) => ({ id }),
|
||||
INPUT_OTP: (otp: string) => ({ otp }),
|
||||
INPUT_ID: (id: string) => ({id}),
|
||||
INPUT_OTP: (otp: string) => ({otp}),
|
||||
RESEND_OTP: () => ({}),
|
||||
VALIDATE_INPUT: () => ({}),
|
||||
READY: (idInputRef: TextInput) => ({ idInputRef }),
|
||||
READY: (idInputRef: TextInput) => ({idInputRef}),
|
||||
DISMISS: () => ({}),
|
||||
SELECT_ID_TYPE: (idType: VcIdType) => ({ idType }),
|
||||
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),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -294,22 +279,17 @@ export const AddVcModalMachine =
|
||||
},
|
||||
}),
|
||||
|
||||
clearId: model.assign({ id: '' }),
|
||||
clearId: model.assign({id: ''}),
|
||||
|
||||
setHashedId: model.assign({
|
||||
hashedId: (_context, event) =>
|
||||
(event as DoneInvokeEvent<string>).data,
|
||||
}),
|
||||
|
||||
clearIdError: model.assign({ idError: '' }),
|
||||
clearIdError: model.assign({idError: ''}),
|
||||
|
||||
setIdErrorEmpty: model.assign({
|
||||
idError: () => i18n.t('errors.input.empty', { ns: 'AddVcModal' }),
|
||||
idError: () => i18n.t('errors.input.empty', {ns: 'AddVcModal'}),
|
||||
}),
|
||||
|
||||
setIdErrorWrongFormat: model.assign({
|
||||
idError: () =>
|
||||
i18n.t('errors.input.invalidFormat', { ns: 'AddVcModal' }),
|
||||
i18n.t('errors.input.invalidFormat', {ns: 'AddVcModal'}),
|
||||
}),
|
||||
|
||||
setOtpError: assign({
|
||||
@@ -335,13 +315,13 @@ export const AddVcModalMachine =
|
||||
idInputRef: null,
|
||||
}),
|
||||
|
||||
clearOtp: assign({ otp: '' }),
|
||||
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,27 +345,16 @@ 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: {
|
||||
isEmptyId: ({ id }) => !id || !id.length,
|
||||
isEmptyId: ({id}) => !id || !id.length,
|
||||
|
||||
isWrongIdFormat: ({ idType, id }) => {
|
||||
isWrongIdFormat: ({idType, id}) => {
|
||||
const validIdType =
|
||||
idType === 'UIN' ? id.length === 10 : id.length === 16;
|
||||
return !(/^\d{10,16}$/.test(id) && validIdType);
|
||||
@@ -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;
|
||||
@@ -28,21 +28,21 @@ export interface Typegen0 {
|
||||
'xstate.after(100)#AddVcModal.acceptingIdInput.focusing': {
|
||||
type: 'xstate.after(100)#AddVcModal.acceptingIdInput.focusing';
|
||||
};
|
||||
'xstate.init': { type: 'xstate.init' };
|
||||
'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'
|
||||
@@ -111,8 +111,8 @@ export interface Typegen0 {
|
||||
| 'invalid'
|
||||
| 'rendering'
|
||||
| 'requestingOtp'
|
||||
| { invalid?: 'backend' | 'empty' | 'format' };
|
||||
| {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 (
|
||||
<ActivityLogText
|
||||
key={`${activity.timestamp}-${activity._vcKey}`}
|
||||
activity={activity}
|
||||
/>
|
||||
);
|
||||
}
|
||||
})}
|
||||
{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>;
|
||||
}
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
import React from 'react';
|
||||
import { Icon, Input } from 'react-native-elements';
|
||||
import { Picker } from '@react-native-picker/picker';
|
||||
import { Button, Column, Row, Text } from '../../../components/ui';
|
||||
import { Modal } from '../../../components/ui/Modal';
|
||||
import { Theme } from '../../../components/ui/styleUtils';
|
||||
import { IdInputModalProps, useIdInputModal } from './IdInputModalController';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {Icon, Input} from 'react-native-elements';
|
||||
import {Picker} from '@react-native-picker/picker';
|
||||
import {Button, Column, Row, Text} from '../../../components/ui';
|
||||
import {Modal} from '../../../components/ui/Modal';
|
||||
import {Theme} from '../../../components/ui/styleUtils';
|
||||
import {IdInputModalProps, useIdInputModal} from './IdInputModalController';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {
|
||||
I18nManager,
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
TextInput,
|
||||
} from 'react-native';
|
||||
import { TouchableOpacity } from 'react-native';
|
||||
import { individualId } from '../../../shared/constants';
|
||||
import { GET_INDIVIDUAL_ID } from '../../../shared/constants';
|
||||
import { MessageOverlay } from '../../../components/MessageOverlay';
|
||||
import {TouchableOpacity} from 'react-native';
|
||||
import {individualId} from '../../../shared/constants';
|
||||
import {GET_INDIVIDUAL_ID} from '../../../shared/constants';
|
||||
import {MessageOverlay} from '../../../components/MessageOverlay';
|
||||
import testIDProps from '../../../shared/commonUtil';
|
||||
|
||||
export const IdInputModal: React.FC<IdInputModalProps> = (props) => {
|
||||
const { t } = useTranslation('IdInputModal');
|
||||
export const IdInputModal: React.FC<IdInputModalProps> = props => {
|
||||
const {t} = useTranslation('IdInputModal');
|
||||
const controller = useIdInputModal(props);
|
||||
|
||||
const setIndividualID = () => {
|
||||
@@ -31,7 +31,7 @@ export const IdInputModal: React.FC<IdInputModalProps> = (props) => {
|
||||
GET_INDIVIDUAL_ID('');
|
||||
};
|
||||
|
||||
const inputLabel = t('enterId', { idType: controller.idType });
|
||||
const inputLabel = t('enterId', {idType: controller.idType});
|
||||
|
||||
const setIdInputRef = (node: TextInput) =>
|
||||
!controller.idInputRef && controller.READY(node);
|
||||
@@ -44,7 +44,7 @@ export const IdInputModal: React.FC<IdInputModalProps> = (props) => {
|
||||
headerTitle={t('header')}
|
||||
headerElevation={2}>
|
||||
<KeyboardAvoidingView
|
||||
style={{ flex: 1 }}
|
||||
style={{flex: 1}}
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
|
||||
<Column fill align="space-between" pY={32} pX={24}>
|
||||
<Column>
|
||||
@@ -54,7 +54,7 @@ export const IdInputModal: React.FC<IdInputModalProps> = (props) => {
|
||||
style={Theme.TextStyles.retrieveIdLabel}>
|
||||
{t('guideLabel')}
|
||||
</Text>
|
||||
<Row crossAlign="flex-end" style={{ marginTop: 20 }}>
|
||||
<Row crossAlign="flex-end" style={{marginTop: 20}}>
|
||||
<Column
|
||||
width="33%"
|
||||
style={{
|
||||
|
||||
@@ -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') {
|
||||
return (
|
||||
<VcItem
|
||||
key={`${vcKey}-${index}`}
|
||||
vcKey={vcKey}
|
||||
margin="0 2 8 2"
|
||||
onPress={controller.VIEW_VC}
|
||||
iconName="pushpin"
|
||||
iconType="antdesign"
|
||||
/>
|
||||
);
|
||||
}
|
||||
})}
|
||||
{controller.vcKeys.map((vcKey, index) => {
|
||||
if (vcKey.split(':')[4] === 'false') {
|
||||
return (
|
||||
<VcItem
|
||||
key={`${vcKey}-${index}`}
|
||||
vcKey={vcKey}
|
||||
margin="0 2 8 2"
|
||||
onPress={controller.VIEW_VC}
|
||||
/>
|
||||
);
|
||||
}
|
||||
{vcMetadataOrderedByPinStatus.map((vcMetadata, index) => {
|
||||
const iconProps = vcMetadata.isPinned ? pinIconProps : {};
|
||||
return (
|
||||
<VcItem
|
||||
{...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,19 +1,19 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { RefreshControl } from 'react-native';
|
||||
import { Icon } from 'react-native-elements';
|
||||
import { Centered, Column, Text } from '../../components/ui';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { HomeScreenTabProps } from './HomeScreen';
|
||||
import { useReceivedVcsTab } from './ReceivedVcsTabController';
|
||||
import { VcItem } from '../../components/VcItem';
|
||||
import React, {useEffect} from 'react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {RefreshControl} from 'react-native';
|
||||
import {Icon} from 'react-native-elements';
|
||||
import {Centered, Column, Text} from '../../components/ui';
|
||||
import {Theme} from '../../components/ui/styleUtils';
|
||||
import {HomeScreenTabProps} from './HomeScreen';
|
||||
import {useReceivedVcsTab} from './ReceivedVcsTabController';
|
||||
import {VcItem} from '../../components/VcItem';
|
||||
|
||||
export const ReceivedVcsTab: React.FC<HomeScreenTabProps> = (props) => {
|
||||
const { t } = useTranslation('ReceivedVcsTab');
|
||||
const controller = useReceivedVcsTab(props);
|
||||
export const ReceivedVcsTab: React.FC<HomeScreenTabProps> = props => {
|
||||
const {t} = useTranslation('ReceivedVcsTab');
|
||||
const controller = useReceivedVcsTab();
|
||||
|
||||
return (
|
||||
<Column fill style={{ display: props.isVisible ? 'flex' : 'none' }}>
|
||||
<Column fill style={{display: props.isVisible ? 'flex' : 'none'}}>
|
||||
<Column
|
||||
scroll
|
||||
padding="32 24"
|
||||
@@ -23,20 +23,20 @@ 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
|
||||
style={{ marginBottom: 20 }}
|
||||
style={{marginBottom: 20}}
|
||||
size={40}
|
||||
name="sentiment-dissatisfied"
|
||||
/>
|
||||
|
||||
@@ -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 }),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import React from 'react';
|
||||
import { Platform, View } from 'react-native';
|
||||
import { getVersion } from 'react-native-device-info';
|
||||
import { Icon, ListItem, Switch } from 'react-native-elements';
|
||||
import { Column, Text } from '../../components/ui';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { MainRouteProps } from '../../routes/main';
|
||||
import { EditableListItem } from '../../components/EditableListItem';
|
||||
import { MessageOverlay } from '../../components/MessageOverlay';
|
||||
import { Revoke } from '../Settings/Revoke';
|
||||
import { useProfileScreen } from './ProfileScreenController';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { LanguageSelector } from '../../components/LanguageSelector';
|
||||
import i18next, { SUPPORTED_LANGUAGES } from '../../i18n';
|
||||
import { ScrollView } from 'react-native-gesture-handler';
|
||||
import { AppMetaData } from '../Settings/AppMetaData';
|
||||
import { CREDENTIAL_REGISTRY_EDIT } from 'react-native-dotenv';
|
||||
import {Platform, View} from 'react-native';
|
||||
import {getVersion} from 'react-native-device-info';
|
||||
import {Icon, ListItem, Switch} from 'react-native-elements';
|
||||
import {Column, Text} from '../../components/ui';
|
||||
import {Theme} from '../../components/ui/styleUtils';
|
||||
import {MainRouteProps} from '../../routes/main';
|
||||
import {EditableListItem} from '../../components/EditableListItem';
|
||||
import {MessageOverlay} from '../../components/MessageOverlay';
|
||||
import {Revoke} from '../Settings/Revoke';
|
||||
import {useProfileScreen} from './ProfileScreenController';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {LanguageSelector} from '../../components/LanguageSelector';
|
||||
import i18next, {SUPPORTED_LANGUAGES} from '../../i18n';
|
||||
import {ScrollView} from 'react-native-gesture-handler';
|
||||
import {AppMetaData} from '../Settings/AppMetaData';
|
||||
import {CREDENTIAL_REGISTRY_EDIT} from 'react-native-dotenv';
|
||||
|
||||
const LanguageSetting: React.FC = () => {
|
||||
const { t } = useTranslation('ProfileScreen');
|
||||
const {t} = useTranslation('ProfileScreen');
|
||||
return (
|
||||
<LanguageSelector
|
||||
triggerComponent={
|
||||
@@ -42,8 +42,8 @@ const LanguageSetting: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const ProfileScreen: React.FC<MainRouteProps> = (props) => {
|
||||
const { t } = useTranslation('ProfileScreen');
|
||||
export const ProfileScreen: React.FC<MainRouteProps> = props => {
|
||||
const {t} = useTranslation('ProfileScreen');
|
||||
|
||||
const controller = useProfileScreen(props);
|
||||
return (
|
||||
@@ -115,7 +115,7 @@ export const ProfileScreen: React.FC<MainRouteProps> = (props) => {
|
||||
/>
|
||||
</ListItem>
|
||||
{/* Intentionally hidden using {display:'none'} - Refer mosip/inji/issue#607 */}
|
||||
<ListItem bottomDivider disabled style={{ display: 'none' }}>
|
||||
<ListItem bottomDivider disabled style={{display: 'none'}}>
|
||||
<Icon
|
||||
name="unlock"
|
||||
size={20}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useMachine, useSelector } from '@xstate/react';
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import {useMachine, useSelector} from '@xstate/react';
|
||||
import {useContext, useEffect, useState} from 'react';
|
||||
import * as LocalAuthentication from 'expo-local-authentication';
|
||||
import { selectBackendInfo } from '../../machines/app';
|
||||
import {selectBackendInfo} from '../../machines/app';
|
||||
import {
|
||||
AuthEvents,
|
||||
selectBiometrics,
|
||||
@@ -22,12 +22,12 @@ import {
|
||||
selectIsSuccess,
|
||||
selectUnenrolledNotice,
|
||||
} from '../../machines/biometrics';
|
||||
import { MainRouteProps } from '../../routes/main';
|
||||
import { GlobalContext } from '../../shared/GlobalContext';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {MainRouteProps} from '../../routes/main';
|
||||
import {GlobalContext} from '../../shared/GlobalContext';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
|
||||
export function useProfileScreen({ navigation }: MainRouteProps) {
|
||||
const { appService } = useContext(GlobalContext);
|
||||
export function useProfileScreen({navigation}: MainRouteProps) {
|
||||
const {appService} = useContext(GlobalContext);
|
||||
const authService = appService.children.get('auth');
|
||||
const settingsService = appService.children.get('settings');
|
||||
|
||||
@@ -40,9 +40,9 @@ export function useProfileScreen({ navigation }: MainRouteProps) {
|
||||
const errorMsgBio: string = useSelector(bioService, selectError);
|
||||
const unEnrolledNoticeBio: string = useSelector(
|
||||
bioService,
|
||||
selectUnenrolledNotice
|
||||
selectUnenrolledNotice,
|
||||
);
|
||||
const { t } = useTranslation('AuthScreen');
|
||||
const {t} = useTranslation('AuthScreen');
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(async () => {
|
||||
@@ -75,12 +75,12 @@ export function useProfileScreen({ navigation }: MainRouteProps) {
|
||||
settingsService.send(SettingsEvents.TOGGLE_BIOMETRIC_UNLOCK(true));
|
||||
|
||||
// but if device does not have any enrolled biometrics
|
||||
} else if (biometricState.matches({ failure: 'unenrolled' })) {
|
||||
biometricSend({ type: 'RETRY_AUTHENTICATE' });
|
||||
} else if (biometricState.matches({failure: 'unenrolled'})) {
|
||||
biometricSend({type: 'RETRY_AUTHENTICATE'});
|
||||
|
||||
// otherwise lets do a biometric auth
|
||||
} else {
|
||||
biometricSend({ type: 'AUTHENTICATE' });
|
||||
biometricSend({type: 'AUTHENTICATE'});
|
||||
}
|
||||
} else {
|
||||
authService.send(AuthEvents.SETUP_BIOMETRICS(''));
|
||||
@@ -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) => (
|
||||
<VcItem
|
||||
key={vcKey}
|
||||
vcKey={vcKey}
|
||||
margin="0 2 8 2"
|
||||
onPress={controller.SELECT_VC_ITEM(index)}
|
||||
showOnlyBindedVc
|
||||
selectable
|
||||
selected={index === controller.selectedIndex}
|
||||
/>
|
||||
))}
|
||||
{controller.shareableVcsMetadata.length > 0 &&
|
||||
controller.shareableVcsMetadata.map(
|
||||
(vcMetadata, index) => (
|
||||
<VcItem
|
||||
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),
|
||||
|
||||
@@ -10,8 +10,7 @@ import getAllConfigurations from '../../shared/commonprops/commonProps';
|
||||
import {getVersion} from 'react-native-device-info';
|
||||
import {CopyButton} from '../../components/CopyButton';
|
||||
import testIDProps from '../../shared/commonUtil';
|
||||
import { __InjiVersion, __TuvaliVersion } from '../../shared/GlobalVariables';
|
||||
|
||||
import {__InjiVersion, __TuvaliVersion} from '../../shared/GlobalVariables';
|
||||
|
||||
export const AboutInji: React.FC<AboutInjiProps> = ({appId}) => {
|
||||
const {t} = useTranslation('AboutInji');
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, {useState} from 'react';
|
||||
import Markdown from 'react-native-simple-markdown';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { I18nManager, SafeAreaView, View } from 'react-native';
|
||||
import { Divider, Icon, ListItem, Overlay } from 'react-native-elements';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {I18nManager, SafeAreaView, View} from 'react-native';
|
||||
import {Divider, Icon, ListItem, Overlay} from 'react-native-elements';
|
||||
|
||||
import { Button, Text, Row } from '../../components/ui';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import {Button, Text, Row} from '../../components/ui';
|
||||
import {Theme} from '../../components/ui/styleUtils';
|
||||
import appMetaData from '../../AppMetaData.md';
|
||||
import { __InjiVersion, __TuvaliVersion } from '../../shared/GlobalVariables';
|
||||
import {__InjiVersion, __TuvaliVersion} from '../../shared/GlobalVariables';
|
||||
|
||||
export const AppMetaData: React.FC<AppMetaDataProps> = (props) => {
|
||||
const { t } = useTranslation('AppMetaData');
|
||||
export const AppMetaData: React.FC<AppMetaDataProps> = props => {
|
||||
const {t} = useTranslation('AppMetaData');
|
||||
const [isViewing, setIsViewing] = useState(false);
|
||||
|
||||
const markdownStyles = {
|
||||
@@ -37,7 +37,7 @@ export const AppMetaData: React.FC<AppMetaDataProps> = (props) => {
|
||||
</ListItem.Title>
|
||||
</ListItem.Content>
|
||||
<Overlay
|
||||
overlayStyle={{ padding: 24 }}
|
||||
overlayStyle={{padding: 24}}
|
||||
isVisible={isViewing}
|
||||
onBackdropPress={() => setIsViewing(false)}>
|
||||
<SafeAreaView>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -6,25 +6,25 @@ import {
|
||||
SafeAreaView,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import { Divider, Icon, ListItem, Overlay } from 'react-native-elements';
|
||||
import { Button, Column, Centered, Row, Text } from '../../components/ui';
|
||||
import { VidItem } from '../../components/VidItem';
|
||||
import { Theme } from '../../components/ui/styleUtils';
|
||||
import { ToastItem } from '../../components/ui/ToastItem';
|
||||
import { OIDcAuthenticationOverlay } from '../../components/OIDcAuthModal';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useRevoke } from './RevokeController';
|
||||
import {Divider, Icon, ListItem, Overlay} from 'react-native-elements';
|
||||
import {Button, Column, Centered, Row, Text} from '../../components/ui';
|
||||
import {VidItem} from '../../components/VidItem';
|
||||
import {Theme} from '../../components/ui/styleUtils';
|
||||
import {ToastItem} from '../../components/ui/ToastItem';
|
||||
import {OIDcAuthenticationOverlay} from '../../components/OIDcAuthModal';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {useRevoke} from './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');
|
||||
const {t} = useTranslation('ProfileScreen');
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
bottomDivider
|
||||
onPress={() => controller.setAuthenticating(true)}
|
||||
style={{ display: 'none' }}>
|
||||
style={{display: 'none'}}>
|
||||
<Icon
|
||||
name={props.Icon}
|
||||
type="font-awesome"
|
||||
@@ -37,7 +37,7 @@ export const Revoke: React.FC<RevokeScreenProps> = (props) => {
|
||||
</ListItem.Title>
|
||||
</ListItem.Content>
|
||||
<Overlay
|
||||
overlayStyle={{ padding: 0 }}
|
||||
overlayStyle={{padding: 0}}
|
||||
isVisible={controller.isViewing}
|
||||
onBackdropPress={() => controller.setIsViewing(false)}>
|
||||
<SafeAreaView>
|
||||
@@ -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) => (
|
||||
<VidItem
|
||||
key={`${vcKey}-${index}`}
|
||||
vcKey={vcKey}
|
||||
margin="0 2 8 2"
|
||||
onPress={controller.selectVcItem(index, vcKey)}
|
||||
selectable
|
||||
selected={controller.selectedVidKeys.includes(vcKey)}
|
||||
/>
|
||||
))}
|
||||
{controller.uniqueVidsMetadata.map((vcMetadata, index) => {
|
||||
return (
|
||||
<VidItem
|
||||
key={`${vcMetadata.getVcKey()}-${index}`}
|
||||
vcMetadata={vcMetadata}
|
||||
margin="0 2 8 2"
|
||||
onPress={controller.selectVcItem(
|
||||
index,
|
||||
vcMetadata.getVcKey(),
|
||||
)}
|
||||
selectable
|
||||
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),
|
||||
);
|
||||
},
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import {Theme} from '../components/ui/styleUtils';
|
||||
import {Icon} from 'react-native-elements';
|
||||
import {RootRouteProps} from '../routes';
|
||||
import {useWelcomeScreen} from './WelcomeScreenController';
|
||||
import { __SelectedLanguage } from '../shared/GlobalVariables';
|
||||
import {__SelectedLanguage} from '../shared/GlobalVariables';
|
||||
|
||||
export const SetupLanguageScreen: React.FC<RootRouteProps> = props => {
|
||||
const {t} = useTranslation('SetupLanguage');
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { getVersion } from 'react-native-device-info';
|
||||
import {getVersion} from 'react-native-device-info';
|
||||
import ShortUniqueId from 'short-unique-id';
|
||||
import { APP_ID_LENGTH } from './constants';
|
||||
import {APP_ID_LENGTH} from './constants';
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
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') {
|
||||
@@ -20,7 +20,7 @@ function getTuvaliPackageDetails() {
|
||||
}
|
||||
}
|
||||
});
|
||||
return { packageVersion, packageCommitId };
|
||||
return {packageVersion, packageCommitId};
|
||||
}
|
||||
export class __AppId {
|
||||
private static value: string;
|
||||
|
||||
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));
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Platform } from 'react-native';
|
||||
import {Platform} from 'react-native';
|
||||
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;
|
||||
@@ -20,6 +20,6 @@ export interface Argon2iConfig {
|
||||
|
||||
export default function testIDProps(id) {
|
||||
return Platform.OS === 'android'
|
||||
? { accessible: true, accessibilityLabel: id }
|
||||
: { testID: id };
|
||||
? {accessible: true, accessibilityLabel: id}
|
||||
: {testID: id};
|
||||
}
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import { Platform } from 'react-native';
|
||||
import { VC } from '../types/vc';
|
||||
import {Platform} from 'react-native';
|
||||
import {
|
||||
MIMOTO_HOST,
|
||||
ESIGNET_HOST,
|
||||
GOOGLE_NEARBY_MESSAGES_API_KEY,
|
||||
} from 'react-native-dotenv';
|
||||
import { Argon2iConfig } from './commonUtil';
|
||||
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];
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { DecodedCredential, VerifiableCredential } from '../types/vc';
|
||||
import { __AppId } from './GlobalVariables';
|
||||
import { HOST, MIMOTO_BASE_URL } from './constants';
|
||||
import {DecodedCredential, VerifiableCredential} from '../types/vc';
|
||||
import {__AppId} from './GlobalVariables';
|
||||
import {HOST, MIMOTO_BASE_URL} from './constants';
|
||||
|
||||
export class BackendResponseError extends Error {
|
||||
constructor(name: string, message: string) {
|
||||
@@ -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,21 +36,21 @@ export async function request(
|
||||
'The backend API ' +
|
||||
backendUrl +
|
||||
' returned error code 400 with message --> ' +
|
||||
errorMessage
|
||||
errorMessage,
|
||||
);
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
if (jsonResponse.errors && jsonResponse.errors.length) {
|
||||
let backendUrl = host + path;
|
||||
const { errorCode, errorMessage } = jsonResponse.errors.shift();
|
||||
const {errorCode, errorMessage} = jsonResponse.errors.shift();
|
||||
console.error(
|
||||
'The backend API ' +
|
||||
backendUrl +
|
||||
' 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 {MMKVLoader} from 'react-native-mmkv-storage';
|
||||
import CryptoJS from 'crypto-js';
|
||||
import {
|
||||
DocumentDirectoryPath,
|
||||
@@ -11,7 +10,7 @@ import {
|
||||
writeFile,
|
||||
} from 'react-native-fs';
|
||||
import getAllConfigurations from './commonprops/commonProps';
|
||||
import { Platform } from 'react-native';
|
||||
import {Platform} from 'react-native';
|
||||
import {
|
||||
getFreeDiskStorageOldSync,
|
||||
getFreeDiskStorageSync,
|
||||
@@ -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;
|
||||
|
||||
23
types/vc.ts
23
types/vc.ts
@@ -1,4 +1,4 @@
|
||||
import { WalletBindingResponse } from '../shared/cryptoutil/cryptoUtil';
|
||||
import {WalletBindingResponse} from '../shared/cryptoutil/cryptoUtil';
|
||||
|
||||
export interface VC {
|
||||
id: string;
|
||||
@@ -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