mirror of
https://github.com/mosip/inji-wallet.git
synced 2026-01-09 13:38:01 -05:00
lock unlock VC
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useRef } from 'react';
|
||||
import { FlatList, Pressable, View } from 'react-native';
|
||||
import { Popable } from 'react-native-popable';
|
||||
import { Text } from 'react-native-elements';
|
||||
@@ -7,7 +7,12 @@ import { Row } from './ui';
|
||||
import { Colors } from './ui/styleUtils';
|
||||
|
||||
export const DropdownIcon: React.FC<DropdownProps> = (props) => {
|
||||
//const [visible, setVisible] = useState(false);
|
||||
const popover = useRef(null);
|
||||
|
||||
const handleOnPress = (item: Item) => {
|
||||
popover.current?.hide();
|
||||
item.onPress();
|
||||
};
|
||||
|
||||
const renderItem = ({ item }) => {
|
||||
return (
|
||||
@@ -20,7 +25,7 @@ export const DropdownIcon: React.FC<DropdownProps> = (props) => {
|
||||
borderBottomWidth: 1,
|
||||
}}>
|
||||
<Pressable
|
||||
onPress={item.onPress}
|
||||
onPress={() => handleOnPress(item)}
|
||||
style={{ paddingTop: 8, paddingBottom: 8 }}>
|
||||
<Row>
|
||||
<Icon
|
||||
@@ -40,6 +45,7 @@ export const DropdownIcon: React.FC<DropdownProps> = (props) => {
|
||||
<View>
|
||||
<Popable
|
||||
position="bottom"
|
||||
ref={popover}
|
||||
backgroundColor={Colors.White}
|
||||
style={{ top: 10, left: -20, minWidth: 120, elevation: 1 }}
|
||||
content={
|
||||
@@ -56,12 +62,12 @@ export const DropdownIcon: React.FC<DropdownProps> = (props) => {
|
||||
</View>
|
||||
);
|
||||
};
|
||||
// interface item {
|
||||
// label: string;
|
||||
// onSelect: () => void;
|
||||
// }
|
||||
interface Item {
|
||||
label: string;
|
||||
onPress?: () => void;
|
||||
}
|
||||
|
||||
interface DropdownProps {
|
||||
icon: string;
|
||||
items?: any;
|
||||
items: Item[];
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { assign, ErrorPlatformEvent, EventFrom, send, StateFrom } from 'xstate';
|
||||
import { log } from 'xstate/lib/actions';
|
||||
import { createModel } from 'xstate/lib/model';
|
||||
import { VC_ITEM_STORE_KEY } from '../shared/constants';
|
||||
import { AppServices } from '../shared/GlobalContext';
|
||||
@@ -25,8 +26,11 @@ const model = createModel(
|
||||
requestId: '',
|
||||
isVerified: false,
|
||||
lastVerifiedOn: null,
|
||||
locked: false,
|
||||
otp: '',
|
||||
otpError: '',
|
||||
idError: '',
|
||||
transactionId: '',
|
||||
},
|
||||
{
|
||||
events: {
|
||||
@@ -40,7 +44,8 @@ const model = createModel(
|
||||
DOWNLOAD_READY: () => ({}),
|
||||
GET_VC_RESPONSE: (vc: VC) => ({ vc }),
|
||||
VERIFY: () => ({}),
|
||||
LOCKING_VC: () => ({}),
|
||||
LOCK_VC: () => ({}),
|
||||
UNLOCK_VC: () => ({}),
|
||||
INPUT_OTP: (otp: string) => ({ otp }),
|
||||
LOCK: (value: boolean) => ({ value }),
|
||||
},
|
||||
@@ -144,7 +149,14 @@ export const vcItemMachine =
|
||||
VERIFY: {
|
||||
target: 'verifyingCredential',
|
||||
},
|
||||
LOCKING_VC: 'lockingVc',
|
||||
LOCK_VC: {
|
||||
actions: ['setTransactionId'],
|
||||
target: 'requestingOtp',
|
||||
},
|
||||
UNLOCK_VC: {
|
||||
actions: ['setTransactionId'],
|
||||
target: 'requestingOtp',
|
||||
},
|
||||
},
|
||||
},
|
||||
editingTag: {
|
||||
@@ -196,44 +208,33 @@ export const vcItemMachine =
|
||||
},
|
||||
],
|
||||
},
|
||||
// invalid: {
|
||||
// entry: ['focusInput'],
|
||||
// on: {
|
||||
// INPUT_ID: {
|
||||
// target: 'idle',
|
||||
// actions: ['setId', 'clearIdError'],
|
||||
// },
|
||||
// VALIDATE_INPUT: [
|
||||
// { cond: 'isEmptyId', target: '.empty' },
|
||||
// {
|
||||
// cond: 'isWrongIdFormat',
|
||||
// target: '.format',
|
||||
// },
|
||||
// { target: 'requestingOtp' },
|
||||
// ],
|
||||
// },
|
||||
// states: {
|
||||
// empty: {
|
||||
// entry: ['setIdErrorEmpty'],
|
||||
// },
|
||||
// format: {
|
||||
// entry: ['setIdErrorWrongFormat'],
|
||||
// },
|
||||
// backend: {},
|
||||
// },
|
||||
// },
|
||||
invalid: {
|
||||
states: {
|
||||
empty: {
|
||||
entry: [log('UPDATE_SERVICE_URL received')],
|
||||
},
|
||||
backend: {},
|
||||
},
|
||||
on: {
|
||||
INPUT_OTP: {
|
||||
target: 'requestingLock',
|
||||
actions: ['setOtp'],
|
||||
},
|
||||
DISMISS: 'idle',
|
||||
},
|
||||
},
|
||||
requestingOtp: {
|
||||
invoke: {
|
||||
src: 'requestOtp',
|
||||
onDone: '#acceptingOtpInput',
|
||||
onError: {
|
||||
target: 'idle',
|
||||
actions: ['setIdError'],
|
||||
target: 'invalid.empty',
|
||||
},
|
||||
},
|
||||
},
|
||||
acceptingOtpInput: {
|
||||
id: 'acceptingOtpInput',
|
||||
entry: ['clearOtp'],
|
||||
on: {
|
||||
INPUT_OTP: {
|
||||
target: 'requestingLock',
|
||||
@@ -242,21 +243,12 @@ export const vcItemMachine =
|
||||
DISMISS: 'idle',
|
||||
},
|
||||
},
|
||||
lockingVc: {
|
||||
on: {
|
||||
DISMISS: 'idle',
|
||||
INPUT_OTP: {
|
||||
target: 'requestingLock',
|
||||
actions: ['setOtp'],
|
||||
},
|
||||
},
|
||||
},
|
||||
requestingLock: {
|
||||
invoke: {
|
||||
src: 'requestLock',
|
||||
onDone: {
|
||||
target: 'lockVc',
|
||||
actions: ['lockVc'],
|
||||
target: 'lockingVc',
|
||||
actions: ['setLock'],
|
||||
},
|
||||
onError: [
|
||||
{
|
||||
@@ -266,20 +258,12 @@ export const vcItemMachine =
|
||||
],
|
||||
},
|
||||
},
|
||||
lockVc: {
|
||||
lockingVc: {
|
||||
entry: ['storeLock'],
|
||||
on: {
|
||||
STORE_RESPONSE: 'idle',
|
||||
},
|
||||
},
|
||||
storingVcLock: {
|
||||
entry: 'storeVc',
|
||||
on: {
|
||||
STORE_RESPONSE: {
|
||||
target: 'idle',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -363,6 +347,10 @@ export const vcItemMachine =
|
||||
};
|
||||
}),
|
||||
|
||||
setTransactionId: model.assign({
|
||||
transactionId: () => String(new Date().valueOf()).substring(3, 13),
|
||||
}),
|
||||
|
||||
setOtp: model.assign({
|
||||
otp: (_, event) => event.otp,
|
||||
}),
|
||||
@@ -372,6 +360,12 @@ export const vcItemMachine =
|
||||
(event as ErrorPlatformEvent).data.message,
|
||||
}),
|
||||
|
||||
clearOtp: assign({ otp: '' }),
|
||||
|
||||
setLock: model.assign({
|
||||
locked: (context) => !context.locked,
|
||||
}),
|
||||
|
||||
storeLock: send(
|
||||
(context) => {
|
||||
const { serviceRefs, ...data } = context;
|
||||
@@ -448,25 +442,32 @@ export const vcItemMachine =
|
||||
return verifyCredential(context.verifiableCredential);
|
||||
},
|
||||
|
||||
// requestOtp: async (context) => {
|
||||
// const response = await request('POST', '/req/otp', {
|
||||
// individualId: context.id,
|
||||
// individualIdType: context.idType,
|
||||
// otpChannel: ['EMAIL', 'PHONE'],
|
||||
// transactionID: context.transactionId,
|
||||
// });
|
||||
// return response;
|
||||
// },
|
||||
requestOtp: async (context) => {
|
||||
try {
|
||||
return await request('POST', '/req/otp', {
|
||||
individualId: context.id,
|
||||
individualIdType: context.idType,
|
||||
otpChannel: ['EMAIL', 'PHONE'],
|
||||
transactionID: context.transactionId,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
|
||||
requestLock: async (context) => {
|
||||
const response = await request('POST', '/req/auth-lock', {
|
||||
individualId: context.id,
|
||||
individualIdType: context.idType,
|
||||
otp: context.otp,
|
||||
//transactionID: context.transactionId,
|
||||
authType: ['bio'],
|
||||
});
|
||||
return response.response.requestId;
|
||||
try {
|
||||
return await request('POST', '/req/auth-lock', {
|
||||
individualId: context.id,
|
||||
individualIdType: context.idType,
|
||||
otp: context.otp,
|
||||
transactionID: context.transactionId,
|
||||
authType: ['bio'],
|
||||
...(context.locked && { unlockForSeconds: '120' }),
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -533,3 +534,19 @@ export function selectVerifiableCredential(state: State) {
|
||||
export function selectIsEditingTag(state: State) {
|
||||
return state.matches('editingTag');
|
||||
}
|
||||
|
||||
export function selectOtpError(state: State) {
|
||||
return state.context.otpError;
|
||||
}
|
||||
|
||||
export function selectIsLockingVc(state: State) {
|
||||
return state.matches('lockingVc');
|
||||
}
|
||||
|
||||
export function selectIsAcceptingOtpInput(state: State) {
|
||||
return state.matches('acceptingOtpInput');
|
||||
}
|
||||
|
||||
export function selectIsRequestingOtp(state: State) {
|
||||
return state.matches('requestingOtp');
|
||||
}
|
||||
|
||||
@@ -15,18 +15,20 @@ export interface Typegen0 {
|
||||
| 'CREDENTIAL_DOWNLOADED'
|
||||
| 'done.invoke.vc-item.verifyingCredential:invocation[0]';
|
||||
logDownloaded: 'CREDENTIAL_DOWNLOADED';
|
||||
setTransactionId: 'LOCK_VC' | 'UNLOCK_VC';
|
||||
setTag: 'SAVE_TAG';
|
||||
markVcValid: 'done.invoke.vc-item.verifyingCredential:invocation[0]';
|
||||
logError: 'error.platform.vc-item.verifyingCredential:invocation[0]';
|
||||
setIdError: 'error.platform.vc-item.requestingOtp:invocation[0]';
|
||||
setOtp: 'INPUT_OTP';
|
||||
lockVc: 'done.invoke.vc-item.requestingLock:invocation[0]';
|
||||
setLock: 'done.invoke.vc-item.requestingLock:invocation[0]';
|
||||
setOtpError: 'error.platform.vc-item.requestingLock:invocation[0]';
|
||||
requestVcContext: 'xstate.init';
|
||||
requestStoredContext: 'GET_VC_RESPONSE';
|
||||
storeTag: 'SAVE_TAG';
|
||||
clearOtp:
|
||||
| 'done.invoke.vc-item.requestingOtp:invocation[0]'
|
||||
| 'error.platform.vc-item.requestingLock:invocation[0]';
|
||||
storeLock: 'done.invoke.vc-item.requestingLock:invocation[0]';
|
||||
storeVc: 'xstate.init';
|
||||
};
|
||||
'internalEvents': {
|
||||
'done.invoke.vc-item.verifyingCredential:invocation[0]': {
|
||||
@@ -38,10 +40,6 @@ export interface Typegen0 {
|
||||
type: 'error.platform.vc-item.verifyingCredential:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'error.platform.vc-item.requestingOtp:invocation[0]': {
|
||||
type: 'error.platform.vc-item.requestingOtp:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'done.invoke.vc-item.requestingLock:invocation[0]': {
|
||||
type: 'done.invoke.vc-item.requestingLock:invocation[0]';
|
||||
data: unknown;
|
||||
@@ -51,6 +49,15 @@ export interface Typegen0 {
|
||||
type: 'error.platform.vc-item.requestingLock:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'error.platform.vc-item.requestingOtp:invocation[0]': {
|
||||
type: 'error.platform.vc-item.requestingOtp:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'done.invoke.vc-item.requestingOtp:invocation[0]': {
|
||||
type: 'done.invoke.vc-item.requestingOtp:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'': { type: '' };
|
||||
'xstate.init': { type: 'xstate.init' };
|
||||
'done.invoke.checkStatus': {
|
||||
@@ -80,8 +87,8 @@ export interface Typegen0 {
|
||||
requestLock: 'done.invoke.vc-item.requestingLock:invocation[0]';
|
||||
};
|
||||
'missingImplementations': {
|
||||
actions: 'logError' | 'setIdError' | 'lockVc' | 'storeVc';
|
||||
services: 'requestOtp';
|
||||
actions: 'logError';
|
||||
services: never;
|
||||
guards: never;
|
||||
delays: never;
|
||||
};
|
||||
@@ -89,7 +96,7 @@ export interface Typegen0 {
|
||||
checkStatus: 'xstate.init';
|
||||
downloadCredential: 'DOWNLOAD_READY';
|
||||
verifyCredential: 'VERIFY' | '';
|
||||
requestOtp: 'xstate.init';
|
||||
requestOtp: 'LOCK_VC' | 'UNLOCK_VC';
|
||||
requestLock: 'INPUT_OTP';
|
||||
};
|
||||
'eventsCausingGuards': {
|
||||
@@ -108,12 +115,16 @@ export interface Typegen0 {
|
||||
| 'storingTag'
|
||||
| 'verifyingCredential'
|
||||
| 'checkingVerificationStatus'
|
||||
| 'invalid'
|
||||
| 'invalid.empty'
|
||||
| 'invalid.backend'
|
||||
| 'requestingOtp'
|
||||
| 'acceptingOtpInput'
|
||||
| 'lockingVc'
|
||||
| 'requestingLock'
|
||||
| 'lockVc'
|
||||
| 'storingVcLock'
|
||||
| { checkingServerData?: 'checkingStatus' | 'downloadingCredential' };
|
||||
| 'lockingVc'
|
||||
| {
|
||||
checkingServerData?: 'checkingStatus' | 'downloadingCredential';
|
||||
invalid?: 'empty' | 'backend';
|
||||
};
|
||||
'tags': never;
|
||||
}
|
||||
|
||||
@@ -5,14 +5,24 @@ import { Modal } from '../../components/ui/Modal';
|
||||
import { Colors } from '../../components/ui/styleUtils';
|
||||
import { VcDetails } from '../../components/VcDetails';
|
||||
import { DropdownIcon } from '../../components/DropdownIcon';
|
||||
import { MessageOverlay } from '../../components/MessageOverlay';
|
||||
import { OtpVerificationModal } from './MyVcs/OtpVerificationModal';
|
||||
import { useViewVcModal, ViewVcModalProps } from './ViewVcModalController';
|
||||
|
||||
export const ViewVcModal: React.FC<ViewVcModalProps> = (props) => {
|
||||
const controller = useViewVcModal(props);
|
||||
|
||||
const lockVc = () => {
|
||||
if (controller.vc.locked) {
|
||||
controller.UNLOCK_VC();
|
||||
} else {
|
||||
controller.LOCK_VC();
|
||||
}
|
||||
};
|
||||
|
||||
const DATA = [
|
||||
{
|
||||
label: 'Lock',
|
||||
label: controller.vc.locked ? 'Unlock' : 'Lock',
|
||||
icon: 'lock-outline',
|
||||
onPress: () => lockVc(),
|
||||
},
|
||||
@@ -31,10 +41,6 @@ export const ViewVcModal: React.FC<ViewVcModalProps> = (props) => {
|
||||
},
|
||||
];
|
||||
|
||||
const lockVc = () => {
|
||||
controller.LOCKING_VC();
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isVisible={props.isVisible}
|
||||
@@ -55,6 +61,19 @@ export const ViewVcModal: React.FC<ViewVcModalProps> = (props) => {
|
||||
onDismiss={controller.DISMISS}
|
||||
onSave={controller.SAVE_TAG}
|
||||
/>
|
||||
|
||||
<OtpVerificationModal
|
||||
isVisible={controller.isAcceptingOtpInput}
|
||||
onDismiss={controller.DISMISS}
|
||||
onInputDone={controller.INPUT_OTP}
|
||||
error={controller.otpError}
|
||||
/>
|
||||
|
||||
<MessageOverlay
|
||||
isVisible={controller.isRequestingOtp}
|
||||
title="Requesting OTP..."
|
||||
hasProgress
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,8 +3,10 @@ import { ActorRefFrom } from 'xstate';
|
||||
import { ModalProps } from '../../components/ui/Modal';
|
||||
import {
|
||||
selectOtpError,
|
||||
selectIsAcceptingOtpInput,
|
||||
selectIsEditingTag,
|
||||
selectIsLockingVc,
|
||||
selectIsRequestingOtp,
|
||||
selectVc,
|
||||
VcItemEvents,
|
||||
vcItemMachine,
|
||||
@@ -17,12 +19,15 @@ export function useViewVcModal({ vcItemActor }: ViewVcModalProps) {
|
||||
|
||||
isEditingTag: useSelector(vcItemActor, selectIsEditingTag),
|
||||
selectIsLockingVc: useSelector(vcItemActor, selectIsLockingVc),
|
||||
isAcceptingOtpInput: useSelector(vcItemActor, selectIsAcceptingOtpInput),
|
||||
isRequestingOtp: useSelector(vcItemActor, selectIsRequestingOtp),
|
||||
|
||||
EDIT_TAG: () => vcItemActor.send(VcItemEvents.EDIT_TAG()),
|
||||
SAVE_TAG: (tag: string) => vcItemActor.send(VcItemEvents.SAVE_TAG(tag)),
|
||||
DISMISS: () => vcItemActor.send(VcItemEvents.DISMISS()),
|
||||
LOCK: (value: boolean) => vcItemActor.send(VcItemEvents.LOCK(value)),
|
||||
LOCKING_VC: () => vcItemActor.send(VcItemEvents.LOCKING_VC()),
|
||||
LOCK_VC: () => vcItemActor.send(VcItemEvents.LOCK_VC()),
|
||||
UNLOCK_VC: () => vcItemActor.send(VcItemEvents.UNLOCK_VC()),
|
||||
INPUT_OTP: (otp: string) => vcItemActor.send(VcItemEvents.INPUT_OTP(otp)),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ export interface VC {
|
||||
isVerified: boolean;
|
||||
lastVerifiedOn: number;
|
||||
reason?: string;
|
||||
locked: boolean;
|
||||
}
|
||||
|
||||
export type VcIdType = 'UIN' | 'VID';
|
||||
|
||||
Reference in New Issue
Block a user