Skip to content
Merged
4 changes: 2 additions & 2 deletions app/scripts/bundle-analyze-ci.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ if (!platform || !['android', 'ios'].includes(platform)) {
// Bundle size thresholds in MB - easy to update!
const BUNDLE_THRESHOLDS_MB = {
// TODO: fix temporary bundle bump
ios: 45,
android: 45,
ios: 46,
android: 46,
};
Comment on lines 17 to 22
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Bundle threshold bump needs explicit tracking (owner + link + revert-by date).

A +1MB CI allowance is fine as a temporary unblock, but the current // TODO: fix temporary bundle bump is too vague and risks making the increase permanent. Please add a tracking reference (issue/PR), an owner, and a concrete “revert to 45MB by ” note (or a clearly justified new baseline). Based on learnings, keep bundle size impact acceptable and prefer tree-shaking/size optimization over moving thresholds.

Proposed tweak
 const BUNDLE_THRESHOLDS_MB = {
-  // TODO: fix temporary bundle bump
+  // TODO(<issue-or-pr-link>, <owner>, revert-by YYYY-MM-DD): revert to 45MB once bundle size is reduced
   ios: 46,
   android: 46,
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Bundle size thresholds in MB - easy to update!
const BUNDLE_THRESHOLDS_MB = {
// TODO: fix temporary bundle bump
ios: 45,
android: 45,
ios: 46,
android: 46,
};
// Bundle size thresholds in MB - easy to update!
const BUNDLE_THRESHOLDS_MB = {
// TODO(<issue-or-pr-link>, <owner>, revert-by YYYY-MM-DD): revert to 45MB once bundle size is reduced
ios: 46,
android: 46,
};


function formatBytes(bytes) {
Expand Down
160 changes: 138 additions & 22 deletions app/src/components/homescreen/IdCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.

import type { FC } from 'react';
import React from 'react';
import { Image, StyleSheet } from 'react-native';
import React, { useCallback } from 'react';
import { Dimensions, Image, Pressable, StyleSheet } from 'react-native';

Check warning on line 7 in app/src/components/homescreen/IdCard.tsx

View workflow job for this annotation

GitHub Actions / workspace-lint

'Dimensions' is defined but never used
import LinearGradient from 'react-native-linear-gradient';
import { Text, XStack, YStack } from 'tamagui';
import { Separator, Text, XStack, YStack } from 'tamagui';

Check warning on line 9 in app/src/components/homescreen/IdCard.tsx

View workflow job for this annotation

GitHub Actions / workspace-lint

'Separator' is defined but never used
import { useNavigation } from '@react-navigation/native';

import type { AadhaarData } from '@selfxyz/common';
import type { PassportData } from '@selfxyz/common/types/passport';
Expand All @@ -16,8 +17,18 @@
isKycDocument,
isMRZDocument,
} from '@selfxyz/common/utils/types';
import { WarningTriangleIcon } from '@selfxyz/euclid/dist/components/icons/WarningTriangleIcon';
import { RoundFlag } from '@selfxyz/mobile-sdk-alpha/components';
import { white } from '@selfxyz/mobile-sdk-alpha/constants/colors';
import {
black,

Check warning on line 23 in app/src/components/homescreen/IdCard.tsx

View workflow job for this annotation

GitHub Actions / workspace-lint

'black' is defined but never used
red600,
slate100,

Check warning on line 25 in app/src/components/homescreen/IdCard.tsx

View workflow job for this annotation

GitHub Actions / workspace-lint

'slate100' is defined but never used
slate300,

Check warning on line 26 in app/src/components/homescreen/IdCard.tsx

View workflow job for this annotation

GitHub Actions / workspace-lint

'slate300' is defined but never used
slate400,

Check warning on line 27 in app/src/components/homescreen/IdCard.tsx

View workflow job for this annotation

GitHub Actions / workspace-lint

'slate400' is defined but never used
slate500,

Check warning on line 28 in app/src/components/homescreen/IdCard.tsx

View workflow job for this annotation

GitHub Actions / workspace-lint

'slate500' is defined but never used
white,
yellow500,
} from '@selfxyz/mobile-sdk-alpha/constants/colors';
import { dinot, plexMono } from '@selfxyz/mobile-sdk-alpha/constants/fonts';

import CardBackgroundId1 from '@/assets/images/card_background_id1.png';
Expand All @@ -28,14 +39,21 @@
import CardBackgroundId6 from '@/assets/images/card_background_id6.png';
import DevCardLogo from '@/assets/images/dev_card_logo.svg';
import DevCardWave from '@/assets/images/dev_card_wave.svg';
import LogoGray from '@/assets/images/logo_gray.svg';

Check warning on line 42 in app/src/components/homescreen/IdCard.tsx

View workflow job for this annotation

GitHub Actions / workspace-lint

'LogoGray' is defined but never used
import SelfLogoPending from '@/assets/images/self_logo_pending.svg';
import WaveOverlay from '@/assets/images/wave_overlay.png';
import { getSecurityLevel } from '@/components/homescreen/cardSecurityBadge';
import { cardStyles } from '@/components/homescreen/cardStyles';
import KycIdCard from '@/components/homescreen/KycIdCard';
import { SvgXml } from '@/components/homescreen/SvgXmlWrapper';

Check warning on line 48 in app/src/components/homescreen/IdCard.tsx

View workflow job for this annotation

GitHub Actions / workspace-lint

'SvgXml' is defined but never used
import { useCardDimensions } from '@/hooks/useCardDimensions';
import { getBackgroundIndex } from '@/utils/cardBackgroundSelector';
import { getDocumentAttributes } from '@/utils/documentAttributes';
import {
formatDateFromYYMMDD,

Check warning on line 52 in app/src/components/homescreen/IdCard.tsx

View workflow job for this annotation

GitHub Actions / workspace-lint

'formatDateFromYYMMDD' is defined but never used
getDocumentAttributes,
getNameAndSurname,
} from '@/utils/documentAttributes';
import { registerModalCallbacks } from '@/utils/modalCallbackRegistry';

const CARD_BACKGROUNDS = [
CardBackgroundId1,
Expand Down Expand Up @@ -279,6 +297,7 @@
idDocument: PassportData | AadhaarData | KycData | null;
selected: boolean;
hidden: boolean;
isInactive?: boolean;
}

/**
Expand All @@ -293,7 +312,38 @@
idDocument,
selected,
hidden,
isInactive = false,
}) => {
const navigation = useNavigation();
const navigateToDocumentOnboarding = useCallback(() => {
switch (idDocument?.documentCategory) {
case 'passport':
case 'id_card':
navigation.navigate('DocumentOnboarding');
break;
case 'aadhaar':
navigation.navigate('AadhaarUpload', { countryCode: 'IND' });
break;
}
}, [idDocument?.documentCategory, navigation]);
Comment on lines +317 to +328
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, find the IdCard.tsx file and check its content around lines 317-328
fd "IdCard.tsx" -t f -x wc -l {}

Repository: selfxyz/self

Length of output: 371


🏁 Script executed:

# Get the content around lines 317-328 of IdCard.tsx
fd "IdCard.tsx" -t f -x head -n 330 {} | tail -n 20

Repository: selfxyz/self

Length of output: 497


🏁 Script executed:

# Find the navigation configuration files
fd "navigation" -t d | head -20

Repository: selfxyz/self

Length of output: 142


🏁 Script executed:

# Search for RootStackParamList definition
rg -n "RootStackParamList" --type=ts --type=tsx

Repository: selfxyz/self

Length of output: 83


🏁 Script executed:

# Search for route definitions including DocumentOnboarding and AadhaarUpload
rg -n "DocumentOnboarding|AadhaarUpload" --type=ts --type=tsx -C 2

Repository: selfxyz/self

Length of output: 83


🏁 Script executed:

# Get content of IdCard.tsx around lines 317-328
sed -n '310,335p' ./app/src/components/homescreen/IdCard.tsx

Repository: selfxyz/self

Length of output: 783


🏁 Script executed:

# Search for RootStackParamList with correct syntax
rg -n "RootStackParamList" -t ts

Repository: selfxyz/self

Length of output: 13747


🏁 Script executed:

# Search for route definitions with correct syntax
rg -n "DocumentOnboarding|AadhaarUpload" -t ts -C 2

Repository: selfxyz/self

Length of output: 18902


🏁 Script executed:

# Check navigation folder structure
ls -la ./app/src/navigation/

Repository: selfxyz/self

Length of output: 986


🏁 Script executed:

# Check the imports and first 30 lines of IdCard.tsx
head -n 30 ./app/src/components/homescreen/IdCard.tsx

Repository: selfxyz/self

Length of output: 1080


🏁 Script executed:

# Check if RootStackParamList and NativeStackNavigationProp are already imported in IdCard.tsx
grep -n "import\|RootStackParamList\|NativeStackNavigationProp" ./app/src/components/homescreen/IdCard.tsx | head -20

Repository: selfxyz/self

Length of output: 1312


🏁 Script executed:

# View the navigation index.tsx to confirm the type-safe navigation setup with createStaticNavigation
sed -n '1,80p' ./app/src/navigation/index.tsx

Repository: selfxyz/self

Length of output: 3087


Add type parameter to useNavigation() for type-safe route validation.

useNavigation() at line 317 lacks a generic type argument, so navigation calls won't be validated against RootStackParamList. This is inconsistent with the rest of the codebase and leaves routes vulnerable to silent breakage if names or parameters change.

Proposed fix
+import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
+import type { RootStackParamList } from '@/navigation';
+
-  const navigation = useNavigation();
+  const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList>>();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const navigation = useNavigation();
const navigateToDocumentOnboarding = useCallback(() => {
switch (idDocument?.documentCategory) {
case 'passport':
case 'id_card':
navigation.navigate('DocumentOnboarding');
break;
case 'aadhaar':
navigation.navigate('AadhaarUpload', { countryCode: 'IND' });
break;
}
}, [idDocument?.documentCategory, navigation]);
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import type { RootStackParamList } from '@/navigation';
const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList>>();
const navigateToDocumentOnboarding = useCallback(() => {
switch (idDocument?.documentCategory) {
case 'passport':
case 'id_card':
navigation.navigate('DocumentOnboarding');
break;
case 'aadhaar':
navigation.navigate('AadhaarUpload', { countryCode: 'IND' });
break;
}
}, [idDocument?.documentCategory, navigation]);
🤖 Prompt for AI Agents
In `@app/src/components/homescreen/IdCard.tsx` around lines 317 - 328, The
navigation call in navigateToDocumentOnboarding uses useNavigation() without a
generic, so route names and params aren't type-checked; update the useNavigation
invocation in IdCard.tsx to supply the app's stack param type
(RootStackParamList) so navigation.navigate calls (used in
navigateToDocumentOnboarding) are validated against route names and
params—replace the untyped useNavigation with the generic-typed useNavigation
referencing RootStackParamList (and the appropriate navigation prop type import)
to restore compile-time route safety.


const handleInactivePress = useCallback(() => {
const callbackId = registerModalCallbacks({
onButtonPress: navigateToDocumentOnboarding,
onModalDismiss: () => {},
});

navigation.navigate('Modal', {
titleText: 'Your ID needs to be reactivated to continue',
bodyText:
'Make sure that you have your document and recovery method ready.',
buttonText: 'Continue',
secondaryButtonText: 'Not now',
callbackId,
});
}, [navigateToDocumentOnboarding, navigation]);

// Early return if document is null
// Call hooks at the top, before any conditional returns
const {
cardWidth,
Expand Down Expand Up @@ -399,7 +449,47 @@
const truncatedId = getTruncatedId();

return (
<YStack width="100%" alignItems="center" justifyContent="center">
// Container wrapper to handle shadow space properly
<YStack
width="100%" // Add space for horizontal margins
alignItems="center"
justifyContent="center"
>
{isInactive && (
<Pressable
style={styles.inactiveWarningContainer}
onPress={handleInactivePress}
>
<XStack
backgroundColor={red600}
borderRadius={8}
padding={16}
gap={16}
>
<YStack padding={8} backgroundColor={white} borderRadius={8}>
<WarningTriangleIcon color={yellow500} />
</YStack>
<YStack gap={4}>
<Text
color={white}
fontFamily={dinot}
fontSize={16}
fontWeight="500"
>
Your document is inactive
</Text>
<Text
color={white}
fontFamily={dinot}
fontSize={14}
fontWeight="400"
>
Tap here to recover your ID
</Text>
</YStack>
</XStack>
</Pressable>
)}
<YStack
width={cardWidth}
height={cardHeight}
Expand Down Expand Up @@ -566,23 +656,45 @@
</Text>
</YStack>

{/* Security Badge */}
<YStack
backgroundColor="rgba(0, 0, 0, 0.5)"
borderRadius={30}
paddingHorizontal={padding * 0.6}
paddingVertical={padding * 0.3}
>
<Text
fontFamily={dinot}
fontSize={fontSize.badge}
fontWeight="500"
color={white}
textTransform="uppercase"
letterSpacing={0.6}
{/* Bottom Right: Badges */}
<YStack alignItems="flex-end" gap={4}>
{isInactive && (
<YStack
backgroundColor={red600}
borderRadius={30}
paddingHorizontal={padding * 0.6}
paddingVertical={padding * 0.3}
>
<Text
fontFamily={dinot}
fontSize={fontSize.badge}
fontWeight="500"
color={white}
textTransform="uppercase"
letterSpacing={0.6}
>
INACTIVE
</Text>
</YStack>
)}
{/* Security Badge */}
<YStack
backgroundColor="rgba(0, 0, 0, 0.5)"
borderRadius={30}
paddingHorizontal={padding * 0.6}
paddingVertical={padding * 0.3}
>
{securityLevel}
</Text>
<Text
fontFamily={dinot}
fontSize={fontSize.badge}
fontWeight="500"
color={white}
textTransform="uppercase"
letterSpacing={0.6}
>
{securityLevel}
</Text>
</YStack>
</YStack>
</XStack>
</YStack>
Expand All @@ -605,6 +717,10 @@
height: '90%',
opacity: 0.6,
},
inactiveWarningContainer: {
width: '100%',
marginBottom: 16,
},
});

export default IdCardLayout;
3 changes: 3 additions & 0 deletions app/src/components/proof-request/BottomVerifyBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface BottomVerifyBarProps {
isReadyToProve: boolean;
isDocumentExpired: boolean;
testID?: string;
hasCheckedForInactiveDocument: boolean;
}

export const BottomVerifyBar: React.FC<BottomVerifyBarProps> = ({
Expand All @@ -28,6 +29,7 @@ export const BottomVerifyBar: React.FC<BottomVerifyBarProps> = ({
isReadyToProve,
isDocumentExpired,
testID = 'bottom-verify-bar',
hasCheckedForInactiveDocument,
}) => {
const insets = useSafeAreaInsets();

Expand All @@ -46,6 +48,7 @@ export const BottomVerifyBar: React.FC<BottomVerifyBarProps> = ({
isScrollable={isScrollable}
isReadyToProve={isReadyToProve}
isDocumentExpired={isDocumentExpired}
hasCheckedForInactiveDocument={hasCheckedForInactiveDocument}
/>
</View>
);
Expand Down
9 changes: 9 additions & 0 deletions app/src/providers/passportDataProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import type {
import {
brutforceSignatureAlgorithmDsc,
calculateContentHash,
inferDocumentCategory,
} from '@selfxyz/common/utils';
import { parseCertificateSimple } from '@selfxyz/common/utils/certificate_parsing/parseCertificateSimple';
import type {
Expand Down Expand Up @@ -869,6 +870,12 @@ export async function storeDocumentWithDeduplication(
// Store new document using contentHash as service name
await storeDocumentDirectlyToKeychain(contentHash, passportData);

const documentCategory =
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-onboarding inactive documents does not update metadata flag

High Severity

When a user re-onboards an existing document (same content hash), storeDocumentWithDeduplication only updates the stored document data and selection, but does not update the catalog metadata. Specifically, hasExpirationDate is never set on the existing document's metadata. This means old aadhaar documents that were registered before this feature (with hasExpirationDate: undefined) will remain marked as inactive even after the user completes the reactivation flow. The inactive badge prompts users to re-onboard, but re-onboarding the same document won't actually clear the inactive state, creating an unescapable loop.

Additional Locations (1)

Fix in Cursor Fix in Web

passportData.documentCategory ||
inferDocumentCategory(
(passportData as PassportData | AadhaarData).documentType,
);

// Add to catalog
let dataField: string;
if (isMRZDocument(passportData)) {
Expand All @@ -886,6 +893,8 @@ export async function storeDocumentWithDeduplication(
data: dataField,
mock: passportData.mock || false,
isRegistered: false,
hasExpirationDate:
documentCategory === 'id_card' || documentCategory === 'passport',
...(isKycDocument(passportData)
? (() => {
try {
Expand Down
33 changes: 32 additions & 1 deletion app/src/screens/dev/DevSettingsScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.

import React from 'react';
import { ScrollView } from 'react-native';
import { Alert, ScrollView } from 'react-native';
import { YStack } from 'tamagui';
import { useNavigation } from '@react-navigation/native';
import type { NativeStackScreenProps } from '@react-navigation/native-stack';
Expand All @@ -13,6 +13,10 @@ import { useSafeBottomPadding } from '@selfxyz/mobile-sdk-alpha/hooks';
import BugIcon from '@/assets/icons/bug_icon.svg';
import ErrorBoundary from '@/components/ErrorBoundary';
import type { RootStackParamList } from '@/navigation';
import {
loadDocumentCatalogDirectlyFromKeychain,
saveDocumentCatalogDirectlyToKeychain,
} from '@/providers/passportDataProvider';
import { ErrorInjectionSelector } from '@/screens/dev/components/ErrorInjectionSelector';
import { LogLevelSelector } from '@/screens/dev/components/LogLevelSelector';
import { ParameterSection } from '@/screens/dev/components/ParameterSection';
Expand Down Expand Up @@ -52,6 +56,32 @@ const DevSettingsScreen: React.FC = () => {
handleClearPendingVerificationsPress,
} = useDangerZoneActions();

const handleRemoveExpirationDateFlagPress = () => {
Alert.alert(
'Remove Expiration Date Flag',
'Are you sure you want to remove the expiration date flag for the current (selected) document?.',
[
{ text: 'Cancel', style: 'cancel' },
{
text: 'Remove',
style: 'destructive',
onPress: async () => {
const catalog = await loadDocumentCatalogDirectlyFromKeychain();
const selectedDocumentId = catalog.selectedDocumentId;
const selectedDocument = catalog.documents.find(
document => document.id === selectedDocumentId,
);

if (selectedDocument) {
delete selectedDocument.hasExpirationDate;

await saveDocumentCatalogDirectlyToKeychain(catalog);
}
},
},
],
);
};
Comment on lines +59 to +84
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add error handling and success feedback for consistency.

This handler lacks try/catch error handling and success feedback that other danger zone handlers provide (e.g., handleClearPointEventsPress at line 459). While this is dev-only functionality, consistency improves debuggability.

Apply this diff:

          onPress: async () => {
+           try {
              const catalog = await loadDocumentCatalogDirectlyFromKeychain();
              const selectedDocumentId = catalog.selectedDocumentId;
              const selectedDocument = catalog.documents.find(
                document => document.id === selectedDocumentId,
              );

              if (selectedDocument) {
                delete selectedDocument.hasExpirationDate;
-
                await saveDocumentCatalogDirectlyToKeychain(catalog);
+               Alert.alert('Success', 'Expiration date flag removed.', [
+                 { text: 'OK' },
+               ]);
+             } else {
+               Alert.alert('Error', 'No selected document found.', [
+                 { text: 'OK' },
+               ]);
              }
+           } catch (error) {
+             Alert.alert(
+               'Error',
+               error instanceof Error ? error.message : 'Failed to remove flag',
+               [{ text: 'OK' }],
+             );
+           }
          },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleRemoveExpirationDateFlagPress = () => {
Alert.alert(
'Remove Expiration Date Flag',
'Are you sure you want to remove the expiration date flag for the current (selected) document?.',
[
{ text: 'Cancel', style: 'cancel' },
{
text: 'Remove',
style: 'destructive',
onPress: async () => {
const catalog = await loadDocumentCatalogDirectlyFromKeychain();
const selectedDocumentId = catalog.selectedDocumentId;
const selectedDocument = catalog.documents.find(
document => document.id === selectedDocumentId,
);
if (selectedDocument) {
delete selectedDocument.hasExpirationDate;
await saveDocumentCatalogDirectlyToKeychain(catalog);
}
},
},
],
);
};
const handleRemoveExpirationDateFlagPress = () => {
Alert.alert(
'Remove Expiration Date Flag',
'Are you sure you want to remove the expiration date flag for the current (selected) document?.',
[
{ text: 'Cancel', style: 'cancel' },
{
text: 'Remove',
style: 'destructive',
onPress: async () => {
try {
const catalog = await loadDocumentCatalogDirectlyFromKeychain();
const selectedDocumentId = catalog.selectedDocumentId;
const selectedDocument = catalog.documents.find(
document => document.id === selectedDocumentId,
);
if (selectedDocument) {
delete selectedDocument.hasExpirationDate;
await saveDocumentCatalogDirectlyToKeychain(catalog);
Alert.alert('Success', 'Expiration date flag removed.', [
{ text: 'OK' },
]);
} else {
Alert.alert('Error', 'No selected document found.', [
{ text: 'OK' },
]);
}
} catch (error) {
Alert.alert(
'Error',
error instanceof Error ? error.message : 'Failed to remove flag',
[{ text: 'OK' }],
);
}
},
},
],
);
};
🤖 Prompt for AI Agents
In app/src/screens/dev/DevSettingsScreen.tsx around lines 520-545, the
remove-expiration handler lacks error handling and success feedback; wrap the
async work in a try/catch, perform the load/find/delete/save operations inside
the try, and on success call the same success feedback used by other handlers
(e.g., Alert.alert or the existing success UI) to confirm removal; in the catch
log the error (console.error or processLogger) and show an error Alert.alert to
surface the failure, and also handle the case where no selectedDocument is found
by showing an informative alert.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as this is just a dev screen there's no need for it

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shazarre, understood! That makes sense for dev tooling. I'll skip the error handling for this dev-only feature. 👍


🧠 Learnings used
Learnt from: seshanthS
Repo: selfxyz/self PR: 1497
File: app/src/screens/verification/ProveScreen.tsx:125-161
Timestamp: 2025-12-13T18:00:39.400Z
Learning: In app/src/screens/verification/ProveScreen.tsx: The document expiration check using checkDocumentExpiration() is UX-only to prevent wasted gas and provide better user experience. The authoritative expiration validation is enforced in the circuits and smart contracts using trusted time sources (block timestamps), not device clock.

Learnt from: shazarre
Repo: selfxyz/self PR: 1041
File: app/src/providers/passportDataProvider.tsx:297-301
Timestamp: 2025-09-10T14:47:40.945Z
Learning: In app/src/providers/passportDataProvider.tsx: The deleteDocumentDirectlyFromKeychain function is a low-level utility used by the DocumentsAdapter and should not include error handling since callers like deleteDocument() already implement appropriate try/catch with logging for Keychain operations.

Learnt from: CR
Repo: selfxyz/self PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T14:06:55.970Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Always use try-catch for async operations in React Native and TypeScript code.

Learnt from: CR
Repo: selfxyz/self PR: 0
File: app/AGENTS.md:0-0
Timestamp: 2025-11-25T14:08:23.106Z
Learning: Applies to app/**/*.{ts,tsx,js,jsx,swift,kt,java} : Flag security-sensitive operations and note performance implications in code comments

Learnt from: CR
Repo: selfxyz/self PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T14:08:11.887Z
Learning: Flag security-sensitive changes for special review in PR descriptions.

Learnt from: CR
Repo: selfxyz/self PR: 0
File: app/AGENTS.md:0-0
Timestamp: 2025-11-25T14:08:23.106Z
Learning: Applies to app/{ios,android}/** : Document complex native module changes and explain platform-specific code paths

Learnt from: CR
Repo: selfxyz/self PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T14:06:55.970Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Implement certificate validation for passport data verification.

Learnt from: CR
Repo: selfxyz/self PR: 0
File: .cursor/rules/mobile-sdk-migration.mdc:0-0
Timestamp: 2025-11-25T14:07:55.507Z
Learning: Applies to packages/mobile-sdk-alpha/src/index.ts : Re-export all new migrated modules via packages/mobile-sdk-alpha/src/index.ts and document them in packages/mobile-sdk-alpha/README.md

Learnt from: CR
Repo: selfxyz/self PR: 0
File: .cursor/rules/mobile-sdk-migration.mdc:0-0
Timestamp: 2025-11-25T14:07:55.507Z
Learning: Applies to packages/mobile-sdk-alpha/src/processing/**/*.{ts,tsx} : Migrate MRZ processing helpers from app/src/utils/ to packages/mobile-sdk-alpha/src/processing/ with comprehensive tests for MRZ parsing and cross-platform compatibility

Comment on lines +59 to +84
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Minor typo in alert message.

Line 62: '...the current (selected) document?.' — the trailing ?. should be just ?.

Fix
-      'Are you sure you want to remove the expiration date flag for the current (selected) document?.',
+      'Are you sure you want to remove the expiration date flag for the current (selected) document?',
🤖 Prompt for AI Agents
In `@app/src/screens/dev/DevSettingsScreen.tsx` around lines 59 - 84, In
handleRemoveExpirationDateFlagPress, update the Alert.alert message string to
remove the stray period after the question mark (change "'...the current
(selected) document?.'" to use a single question mark). Locate the Alert.alert
call inside handleRemoveExpirationDateFlagPress and edit the second argument
(the confirmation message) to end with "?" rather than "?.", leaving all other
logic (loadDocumentCatalogDirectlyFromKeychain,
saveDocumentCatalogDirectlyToKeychain, deletion of
selectedDocument.hasExpirationDate) unchanged.

return (
<ErrorBoundary>
<ScrollView showsVerticalScrollIndicator={false}>
Expand Down Expand Up @@ -109,6 +139,7 @@ const DevSettingsScreen: React.FC = () => {
onResetBackupState={handleResetBackupStatePress}
onClearBackupEvents={handleClearBackupEventsPress}
onClearPendingKyc={handleClearPendingVerificationsPress}
onRemoveExpirationDateFlag={handleRemoveExpirationDateFlagPress}
/>
</YStack>
</ScrollView>
Expand Down
7 changes: 7 additions & 0 deletions app/src/screens/dev/sections/DangerZoneSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ interface DangerZoneSectionProps {
onResetBackupState: () => void;
onClearBackupEvents: () => void;
onClearPendingKyc: () => void;
onRemoveExpirationDateFlag: () => void;
}

export const DangerZoneSection: React.FC<DangerZoneSectionProps> = ({
Expand All @@ -32,6 +33,7 @@ export const DangerZoneSection: React.FC<DangerZoneSectionProps> = ({
onResetBackupState,
onClearBackupEvents,
onClearPendingKyc,
onRemoveExpirationDateFlag,
}) => {
const dangerActions = [
{
Expand Down Expand Up @@ -64,6 +66,11 @@ export const DangerZoneSection: React.FC<DangerZoneSectionProps> = ({
onPress: onClearPendingKyc,
dangerTheme: true,
},
{
label: 'Remove expiration date flag',
onPress: onRemoveExpirationDateFlag,
dangerTheme: true,
},
];

return (
Expand Down
Loading
Loading