SEL-566: Navigate Home based on document validity (#768)

* feat: navigate home if atleast one valid document is present

* update comments

* Review: Remove unnecessary continue statement

* feat: add tracking
This commit is contained in:
Seshanth.S🐺
2025-07-25 23:04:43 +05:30
committed by GitHub
parent a41558a66b
commit 186daa655c
4 changed files with 162 additions and 112 deletions

View File

@@ -42,6 +42,7 @@ import {
import {
checkIfPassportDscIsInTree,
checkPassportSupported,
hasAnyValidRegisteredDocument,
isDocumentNullified,
isUserRegistered,
isUserRegisteredWithAlternativeCSCA,
@@ -186,6 +187,7 @@ interface ProvingState {
_closeConnections: () => void;
_generatePayload: () => Promise<any>;
_handleWebSocketMessage: (event: MessageEvent) => Promise<void>;
_handleRegisterErrorOrFailure: () => void;
_startSocketIOStatusListener: (
receivedUuid: string,
endpointType: EndpointType,
@@ -228,7 +230,7 @@ export const useProvingStore = create<ProvingState>((set, get) => {
) {
setTimeout(() => {
if (navigationRef.isReady()) {
navigationRef.navigate('Launch');
get()._handleRegisterErrorOrFailure();
}
}, 3000);
}
@@ -395,6 +397,23 @@ export const useProvingStore = create<ProvingState>((set, get) => {
}
},
_handleRegisterErrorOrFailure: async () => {
try {
const hasValid = await hasAnyValidRegisteredDocument();
if (navigationRef.isReady()) {
if (hasValid) {
navigationRef.navigate('Home');
} else {
navigationRef.navigate('Launch');
}
}
} catch (error) {
if (navigationRef.isReady()) {
navigationRef.navigate('Launch');
}
}
},
_startSocketIOStatusListener: (
receivedUuid: string,
endpointType: EndpointType,

View File

@@ -18,7 +18,18 @@ import {
import { DocumentCategory } from '@selfxyz/common';
import { poseidon2, poseidon5 } from 'poseidon-lite';
import { DocumentEvents } from '../../consts/analytics';
import {
getAllDocuments,
loadPassportDataAndSecret,
loadSelectedDocument,
setSelectedDocument,
storePassportData,
} from '../../providers/passportDataProvider';
import { useProtocolStore } from '../../stores/protocolStore';
import analytics from '../../utils/analytics';
const { trackEvent } = analytics();
export type PassportSupportStatus =
| 'passport_metadata_missing'
@@ -250,3 +261,128 @@ function formatCSCAPem(cscaPem: string): string {
}
return cleanedPem;
}
export function isPassportDataValid(passportData: PassportData) {
if (!passportData) {
trackEvent(DocumentEvents.VALIDATE_DOCUMENT_FAILED, {
error: 'Passport data is null',
});
return false;
}
if (!passportData.passportMetadata) {
trackEvent(DocumentEvents.VALIDATE_DOCUMENT_FAILED, {
error: 'Passport metadata is null',
});
return false;
}
if (!passportData.passportMetadata.dg1HashFunction) {
trackEvent(DocumentEvents.VALIDATE_DOCUMENT_FAILED, {
mock: passportData.mock,
dsc: passportData.dsc,
error: 'DG1 hash function is null',
});
return false;
}
if (!passportData.passportMetadata.eContentHashFunction) {
trackEvent(DocumentEvents.VALIDATE_DOCUMENT_FAILED, {
mock: passportData.mock,
dsc: passportData.dsc,
documentCategory: passportData.documentCategory,
error: 'EContent hash function is null',
});
return false;
}
if (!passportData.passportMetadata.signedAttrHashFunction) {
trackEvent(DocumentEvents.VALIDATE_DOCUMENT_FAILED, {
mock: passportData.mock,
dsc: passportData.dsc,
documentCategory: passportData.documentCategory,
error: 'Signed attribute hash function is null',
});
return false;
}
return true;
}
export function migratePassportData(passportData: PassportData): PassportData {
const migratedData = { ...passportData } as any;
if (!('documentCategory' in migratedData) || !('mock' in migratedData)) {
if ('documentType' in migratedData && migratedData.documentType) {
migratedData.mock = migratedData.documentType.startsWith('mock');
migratedData.documentCategory = migratedData.documentType.includes(
'passport',
)
? 'passport'
: 'id_card';
} else {
migratedData.documentType = 'passport';
migratedData.documentCategory = 'passport';
migratedData.mock = false;
}
// console.log('Migrated passport data:', migratedData);
}
return migratedData as PassportData;
}
/**
* This function sequentially checks all documents for a valid registered document.
* Since it uses fetch_all and loadSelectedDocument, it cannot be parallelised.
*/
export async function hasAnyValidRegisteredDocument(): Promise<boolean> {
const allDocuments = await getAllDocuments();
for (const documentId of Object.keys(allDocuments)) {
try {
await setSelectedDocument(documentId);
const selectedDocument = await loadSelectedDocument();
if (!selectedDocument) continue;
let { data: passportData } = selectedDocument;
if (!isPassportDataValid(passportData)) {
trackEvent(DocumentEvents.VALIDATE_DOCUMENT_FAILED, {
error: 'Passport data is not valid',
documentId,
});
continue;
}
const migratedPassportData = migratePassportData(passportData);
if (migratedPassportData !== passportData) {
await storePassportData(migratedPassportData);
passportData = migratedPassportData;
}
const environment = migratedPassportData.mock ? 'stg' : 'prod';
const documentCategory = migratedPassportData.documentCategory;
const authorityKeyIdentifier =
migratedPassportData.dsc_parsed?.authorityKeyIdentifier;
if (!authorityKeyIdentifier) {
trackEvent(DocumentEvents.VALIDATE_DOCUMENT_FAILED, {
error: 'Authority key identifier is null',
documentId,
documentCategory,
mock: migratedPassportData.mock,
});
continue;
}
await useProtocolStore
.getState()
[documentCategory].fetch_all(environment, authorityKeyIdentifier);
const passportDataAndSecret = await loadPassportDataAndSecret();
if (!passportDataAndSecret) continue;
const { secret } = JSON.parse(passportDataAndSecret);
const isRegistered = await isUserRegistered(migratedPassportData, secret);
if (isRegistered) {
trackEvent(DocumentEvents.DOCUMENT_VALIDATED, {
documentId,
documentCategory,
mock: migratedPassportData.mock,
});
return true;
}
} catch (error) {
console.error(`Error in hasAnyValidRegisteredDocument: ${error}`);
trackEvent(DocumentEvents.VALIDATE_DOCUMENT_FAILED, {
error: error instanceof Error ? error.message : 'Unknown error',
documentId,
});
}
}
return false;
}