Feat: Show error code in SDK (#500)

* feat: emit `error_code` and `reason` in app

* feat: add `onError` in sdk

* feat: Display reason in app

* lint & fmt

* feat: add scrollview in ProofRequestStatusScreen for long reasons
This commit is contained in:
Seshanth.S🐺
2025-04-15 12:23:57 +05:30
committed by GitHub
parent a3dc3bcfd1
commit 8fe32d8200
8 changed files with 98 additions and 24 deletions

View File

@@ -2,7 +2,7 @@ import React, { useEffect } from 'react';
import { StatusBar, StyleSheet, View } from 'react-native';
import LottieView from 'lottie-react-native';
import { Spinner } from 'tamagui';
import { ScrollView, Spinner } from 'tamagui';
import loadingAnimation from '../../assets/animations/loading/misc.json';
import failAnimation from '../../assets/animations/proof_failed.json';
@@ -23,7 +23,8 @@ import {
} from '../../utils/haptic';
const SuccessScreen: React.FC = () => {
const { selectedApp, disclosureStatus, cleanSelfApp } = useProofInfo();
const { selectedApp, disclosureStatus, discloseError, cleanSelfApp } =
useProofInfo();
const appName = selectedApp?.appName;
const goHome = useHapticNavigation('Home');
@@ -69,6 +70,7 @@ const SuccessScreen: React.FC = () => {
<Info
status={disclosureStatus}
appName={appName === '' ? 'The app' : appName}
reason={discloseError?.reason ?? undefined}
/>
</View>
<PrimaryButton
@@ -109,9 +111,11 @@ function getTitle(status: ProofStatusEnum) {
function Info({
status,
appName,
reason,
}: {
status: ProofStatusEnum;
appName: string;
reason?: string;
}) {
if (status === 'success') {
return (
@@ -122,11 +126,31 @@ function Info({
);
} else if (status === 'failure' || status === 'error') {
return (
<Description>
Unable to prove your identity to{' '}
<BodyText style={typography.strong}>{appName}</BodyText>
{status === 'error' && '. Due to technical issues.'}
</Description>
<View style={{ gap: 8 }}>
<Description>
Unable to prove your identity to{' '}
<BodyText style={typography.strong}>{appName}</BodyText>
{status === 'error' && '. Due to technical issues.'}
</Description>
{status === 'failure' && reason && (
<>
<Description>
<BodyText style={[typography.strong, { fontSize: 14 }]}>
Reason:
</BodyText>
</Description>
<View style={{ maxHeight: 60 }}>
<ScrollView showsVerticalScrollIndicator={true}>
<Description>
<BodyText style={[typography.strong, { fontSize: 14 }]}>
{reason}
</BodyText>
</Description>
</ScrollView>
</View>
</>
)}
</View>
);
} else {
return (

View File

@@ -168,7 +168,9 @@ const ProveScreen: React.FC = () => {
);
handleProofResult(
currentApp.sessionId,
status === ProofStatusEnum.SUCCESS,
status?.status === ProofStatusEnum.SUCCESS,
status?.error_code,
status?.reason,
);
} catch (e) {
console.log('Error in verification process');

View File

@@ -28,7 +28,12 @@ interface IAppContext {
* @param sessionId - The session ID from the scanned QR code.
* @param success - Whether the proof was verified successfully.
*/
handleProofResult: (sessionId: string, success: boolean) => void;
handleProofResult: (
sessionId: string,
success: boolean,
error_code?: string,
reason?: string,
) => void;
}
const AppContext = createContext<IAppContext>({
@@ -120,7 +125,12 @@ export const AppProvider: React.FC<{ children: React.ReactNode }> = ({
}
};
const handleProofResult = (sessionId: string, proof_verified: boolean) => {
const handleProofResult = (
sessionId: string,
proof_verified: boolean,
error_code?: string,
reason?: string,
) => {
console.log(
'[AppProvider] handleProofResult called with sessionId:',
sessionId,
@@ -143,11 +153,15 @@ export const AppProvider: React.FC<{ children: React.ReactNode }> = ({
'[AppProvider] Emitting proof_generation_failed event with data:',
{
session_id: sessionId,
error_code,
reason,
},
);
socketRef.current.emit('proof_generation_failed', {
session_id: sessionId,
error_code,
reason,
});
}
};

View File

@@ -16,9 +16,15 @@ export enum ProofStatusEnum {
ERROR = 'error',
}
export type DiscloseError = {
error_code?: string;
reason?: string;
};
interface IProofContext {
registrationStatus: ProofStatusEnum;
disclosureStatus: ProofStatusEnum;
discloseError: DiscloseError | undefined;
selectedApp: SelfApp;
setSelectedApp: (app: SelfApp) => void;
cleanSelfApp: () => void;
@@ -28,6 +34,7 @@ interface IProofContext {
const defaults: IProofContext = {
registrationStatus: ProofStatusEnum.PENDING,
disclosureStatus: ProofStatusEnum.PENDING,
discloseError: undefined,
selectedApp: {
appName: '',
logoBase64: '',
@@ -52,7 +59,7 @@ export let globalSetRegistrationStatus:
| ((status: ProofStatusEnum) => void)
| null = null;
export let globalSetDisclosureStatus:
| ((status: ProofStatusEnum) => void)
| ((status: ProofStatusEnum, error?: DiscloseError) => void)
| null = null;
/*
@@ -66,6 +73,10 @@ export function ProofProvider({ children }: PropsWithChildren<{}>) {
ProofStatusEnum.PENDING,
);
const [discloseError, setDiscloseError] = useState<DiscloseError | undefined>(
undefined,
);
const [selectedApp, setSelectedAppInternal] = useState<SelfApp>(
defaults.selectedApp,
);
@@ -75,6 +86,7 @@ export function ProofProvider({ children }: PropsWithChildren<{}>) {
return;
}
setRegistrationStatus(ProofStatusEnum.PENDING);
setDiscloseError(undefined);
setSelectedAppInternal(app);
}, []);
@@ -87,11 +99,15 @@ export function ProofProvider({ children }: PropsWithChildren<{}>) {
const resetProof = useCallback(() => {
setRegistrationStatus(ProofStatusEnum.PENDING);
setDisclosureStatus(ProofStatusEnum.PENDING);
setDiscloseError(undefined);
}, []);
useEffect(() => {
globalSetRegistrationStatus = setRegistrationStatus;
globalSetDisclosureStatus = setDisclosureStatus;
globalSetDisclosureStatus = (status, error) => {
setDisclosureStatus(status);
setDiscloseError(error);
};
return () => {
globalSetRegistrationStatus = null;
globalSetDisclosureStatus = null;
@@ -102,6 +118,7 @@ export function ProofProvider({ children }: PropsWithChildren<{}>) {
() => ({
registrationStatus,
disclosureStatus,
discloseError,
selectedApp,
setSelectedApp,
cleanSelfApp,
@@ -110,8 +127,10 @@ export function ProofProvider({ children }: PropsWithChildren<{}>) {
[
registrationStatus,
disclosureStatus,
discloseError,
selectedApp,
setSelectedApp,
setDiscloseError,
cleanSelfApp,
resetProof,
],

View File

@@ -120,7 +120,7 @@ async function checkIdPassportDscIsInTree(
circuitDNSMapping,
endpointType,
);
if (dscStatus !== ProofStatusEnum.SUCCESS) {
if (dscStatus.status !== ProofStatusEnum.SUCCESS) {
console.log('DSC proof failed');
return false;
}
@@ -140,9 +140,9 @@ export async function sendDscPayload(
passportData: PassportData,
circuitDNSMapping: Record<string, string>,
endpointType: EndpointType,
): Promise<ProofStatusEnum | false> {
): Promise<{ status: ProofStatusEnum; error_code?: string; reason?: string }> {
if (!passportData) {
return false;
return { status: ProofStatusEnum.FAILURE };
}
// const isSupported = checkPassportSupported(passportData);
// if (!isSupported) {

View File

@@ -10,6 +10,7 @@ import {
} from '../../../../common/src/constants/constants';
import { EndpointType } from '../../../../common/src/utils/appType';
import {
DiscloseError,
ProofStatusEnum,
globalSetDisclosureStatus,
globalSetRegistrationStatus,
@@ -67,7 +68,7 @@ export async function sendPayload(
updateGlobalOnFailure?: boolean;
flow?: 'registration' | 'disclosure';
},
): Promise<ProofStatusEnum> {
): Promise<{ status: ProofStatusEnum; error_code?: string; reason?: string }> {
const opts = {
updateGlobalOnSuccess: true,
updateGlobalOnFailure: true,
@@ -75,7 +76,11 @@ export async function sendPayload(
};
return new Promise(resolve => {
let finalized = false;
function finalize(status: ProofStatusEnum) {
function finalize(
status: ProofStatusEnum,
error_code?: string,
reason?: string,
) {
if (!finalized) {
finalized = true;
clearTimeout(timer);
@@ -84,12 +89,15 @@ export async function sendPayload(
(status !== ProofStatusEnum.SUCCESS && opts.updateGlobalOnFailure)
) {
if (options?.flow === 'disclosure') {
globalSetDisclosureStatus && globalSetDisclosureStatus(status);
let discloseError: DiscloseError | undefined =
error_code || reason ? { error_code, reason } : undefined;
globalSetDisclosureStatus &&
globalSetDisclosureStatus(status, discloseError);
} else {
globalSetRegistrationStatus && globalSetRegistrationStatus(status);
}
}
resolve(status);
resolve({ status, error_code, reason });
}
}
const uuid = v4();
@@ -201,7 +209,7 @@ export async function sendPayload(
if (ws.readyState === WebSocket.OPEN) {
ws.close();
}
finalize(ProofStatusEnum.FAILURE);
finalize(ProofStatusEnum.FAILURE, data.error_code, data.reason);
}
});
socket.on('disconnect', reason => {