Skip to content
Merged
4 changes: 3 additions & 1 deletion app/src/components/documents/IDSelectorItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface IDSelectorItemProps {
state: IDSelectorState;
onPress?: () => void;
disabled?: boolean;
isLastItem?: boolean;
testID?: string;
}

Expand Down Expand Up @@ -61,6 +62,7 @@ export const IDSelectorItem: React.FC<IDSelectorItemProps> = ({
state,
onPress,
disabled,
isLastItem,
testID,
}) => {
const isDisabled = disabled || isDisabledState(state);
Expand Down Expand Up @@ -116,7 +118,7 @@ export const IDSelectorItem: React.FC<IDSelectorItemProps> = ({
</YStack>
</XStack>
</Pressable>
<Separator borderColor={slate300} />
{!isLastItem && <Separator borderColor={slate300} />}
</>
);
};
Expand Down
20 changes: 7 additions & 13 deletions app/src/components/documents/IDSelectorSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,7 @@
// SPDX-License-Identifier: BUSL-1.1
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.

import {
Button,
ScrollView,
Separator,
Sheet,
Text,
XStack,
YStack,
} from 'tamagui';
import { Button, ScrollView, Sheet, Text, XStack, YStack } from 'tamagui';
import { X } from '@tamagui/lucide-icons';

import {
Expand All @@ -21,6 +13,7 @@ import {
white,
} from '@selfxyz/mobile-sdk-alpha/constants/colors';
import { dinot } from '@selfxyz/mobile-sdk-alpha/constants/fonts';
import { useSafeBottomPadding } from '@selfxyz/mobile-sdk-alpha/hooks';

import type { IDSelectorState } from '@/components/documents/IDSelectorItem';
import {
Expand Down Expand Up @@ -55,6 +48,8 @@ export const IDSelectorSheet: React.FC<IDSelectorSheetProps> = ({
onApprove,
testID = 'id-selector-sheet',
}) => {
const bottomPadding = useSafeBottomPadding(16);

// Check if the selected document is valid (not expired or unregistered)
const selectedDoc = documents.find(d => d.id === selectedId);
const canApprove = selectedDoc && !isDisabledState(selectedDoc.state);
Expand Down Expand Up @@ -105,15 +100,13 @@ export const IDSelectorSheet: React.FC<IDSelectorSheetProps> = ({
</XStack>
</XStack>

<Separator borderColor={slate300} marginBottom="$2" />

{/* Document List */}
<ScrollView
flex={1}
showsVerticalScrollIndicator={false}
testID={`${testID}-list`}
>
{documents.map(doc => {
{documents.map((doc, index) => {
const isSelected = doc.id === selectedId;
// Don't override to 'active' if the document is in a disabled state
const itemState: IDSelectorState =
Expand All @@ -127,14 +120,15 @@ export const IDSelectorSheet: React.FC<IDSelectorSheetProps> = ({
documentName={doc.name}
state={itemState}
onPress={() => onSelect(doc.id)}
isLastItem={index === documents.length - 1}
testID={`${testID}-item-${doc.id}`}
/>
);
})}
</ScrollView>

{/* Footer Buttons */}
<XStack gap={12} marginTop="$4" paddingBottom="$2">
<XStack gap={12} marginTop="$4" paddingBottom={bottomPadding}>
<Button
flex={1}
backgroundColor={white}
Expand Down
2 changes: 1 addition & 1 deletion app/src/components/navbar/HomeNavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const HomeNavBar = (props: NativeStackHeaderProps) => {
try {
Clipboard.setString('');
} catch {}
props.navigation.navigate('Prove');
props.navigation.navigate('ProvingScreenRouter');
} catch (error) {
console.error('Error consuming token:', error);
if (
Expand Down
2 changes: 1 addition & 1 deletion app/src/hooks/useEarnPointsFlow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const useEarnPointsFlow = ({

// Use setTimeout to ensure modal dismisses before navigating
setTimeout(() => {
navigation.navigate('Prove');
navigation.navigate('ProvingScreenRouter');
}, 100);
}, [selfClient, navigation]);

Expand Down
13 changes: 13 additions & 0 deletions app/src/navigation/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import AccountRecoveryScreen from '@/screens/account/recovery/AccountRecoveryScr
import DocumentDataNotFoundScreen from '@/screens/account/recovery/DocumentDataNotFoundScreen';
import RecoverWithPhraseScreen from '@/screens/account/recovery/RecoverWithPhraseScreen';
import CloudBackupScreen from '@/screens/account/settings/CloudBackupScreen';
import { ProofSettingsScreen } from '@/screens/account/settings/ProofSettingsScreen';
import SettingsScreen from '@/screens/account/settings/SettingsScreen';
import ShowRecoveryPhraseScreen from '@/screens/account/settings/ShowRecoveryPhraseScreen';
import { IS_EUCLID_ENABLED } from '@/utils/devUtils';
Expand Down Expand Up @@ -65,6 +66,18 @@ const accountScreens = {
},
} as NativeStackNavigationOptions,
},
ProofSettings: {
screen: ProofSettingsScreen,
options: {
title: 'Proof Settings',
headerStyle: {
backgroundColor: white,
},
headerTitleStyle: {
color: black,
},
} as NativeStackNavigationOptions,
},
Settings: {
screen: SettingsScreen,
options: {
Expand Down
7 changes: 5 additions & 2 deletions app/src/navigation/deeplinks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,10 @@ export const handleUrl = (selfClient: SelfClient, uri: string) => {
selfClient.getSelfAppState().startAppListener(selfAppJson.sessionId);

navigationRef.reset(
createDeeplinkNavigationState('Prove', correctParentScreen),
createDeeplinkNavigationState(
'ProvingScreenRouter',
correctParentScreen,
),
);

return;
Expand All @@ -143,7 +146,7 @@ export const handleUrl = (selfClient: SelfClient, uri: string) => {
selfClient.getSelfAppState().startAppListener(sessionId);

navigationRef.reset(
createDeeplinkNavigationState('Prove', correctParentScreen),
createDeeplinkNavigationState('ProvingScreenRouter', correctParentScreen),
);
} else if (mock_passport) {
try {
Expand Down
5 changes: 5 additions & 0 deletions app/src/navigation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ export type RootStackParamList = Omit<
| 'Disclaimer'
| 'DocumentNFCScan'
| 'DocumentOnboarding'
| 'DocumentSelectorForProving'
| 'ProvingScreenRouter'
| 'Gratification'
| 'Home'
| 'IDPicker'
Expand Down Expand Up @@ -142,13 +144,16 @@ export type RootStackParamList = Omit<
returnToScreen?: 'Points';
}
| undefined;
ProofSettings: undefined;
AccountVerifiedSuccess: undefined;

// Proof/Verification screens
ProofHistoryDetail: {
data: ProofHistory;
};
Prove: undefined;
ProvingScreenRouter: undefined;
DocumentSelectorForProving: undefined;

// App screens
Loading: {
Expand Down
22 changes: 22 additions & 0 deletions app/src/navigation/verification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import type { NativeStackNavigationOptions } from '@react-navigation/native-stac

import { black, white } from '@selfxyz/mobile-sdk-alpha/constants/colors';

import { DocumentSelectorForProvingScreen } from '@/screens/verification/DocumentSelectorForProvingScreen';
import ProofRequestStatusScreen from '@/screens/verification/ProofRequestStatusScreen';
import ProveScreen from '@/screens/verification/ProveScreen';
import { ProvingScreenRouter } from '@/screens/verification/ProvingScreenRouter';
import QRCodeTroubleScreen from '@/screens/verification/QRCodeTroubleScreen';
import QRCodeViewFinderScreen from '@/screens/verification/QRCodeViewFinderScreen';

Expand All @@ -20,6 +22,26 @@ const verificationScreens = {
gestureEnabled: false,
} as NativeStackNavigationOptions,
},
ProvingScreenRouter: {
screen: ProvingScreenRouter,
options: {
headerShown: false,
gestureEnabled: false,
} as NativeStackNavigationOptions,
},
DocumentSelectorForProving: {
screen: DocumentSelectorForProvingScreen,
options: {
title: 'Select ID',
headerStyle: {
backgroundColor: black,
},
headerTitleStyle: {
color: white,
},
gestureEnabled: false,
} as NativeStackNavigationOptions,
},
Prove: {
screen: ProveScreen,
options: {
Expand Down
130 changes: 130 additions & 0 deletions app/src/screens/account/settings/ProofSettingsScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc.
// SPDX-License-Identifier: BUSL-1.1
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.

import React from 'react';
import { StyleSheet, Switch, Text, View } from 'react-native';
import { ScrollView, YStack } from 'tamagui';

import {
black,
blue600,
slate200,
slate500,
white,
} from '@selfxyz/mobile-sdk-alpha/constants/colors';
import { dinot } from '@selfxyz/mobile-sdk-alpha/constants/fonts';

import { useSettingStore } from '@/stores/settingStore';

const ProofSettingsScreen: React.FC = () => {
const {
skipDocumentSelector,
setSkipDocumentSelector,
skipDocumentSelectorIfSingle,
setSkipDocumentSelectorIfSingle,
} = useSettingStore();

return (
<YStack flex={1} backgroundColor={white}>
<ScrollView>
<YStack padding={20} gap={20}>
<Text style={styles.sectionTitle}>Document Selection</Text>

<View style={styles.settingRow}>
<View style={styles.settingTextContainer}>
<Text style={styles.settingLabel}>
Always skip document selection
</Text>
<Text style={styles.settingDescription}>
Go directly to proof generation using your previously selected
or first available document
</Text>
</View>
<Switch
value={skipDocumentSelector}
onValueChange={setSkipDocumentSelector}
trackColor={{ false: slate200, true: blue600 }}
thumbColor={white}
testID="skip-document-selector-toggle"
/>
</View>

<View style={styles.divider} />

<View style={styles.settingRow}>
<View style={styles.settingTextContainer}>
<Text style={styles.settingLabel}>
Skip when only one document
</Text>
<Text style={styles.settingDescription}>
Automatically select your document when you only have one valid
ID available
</Text>
</View>
<Switch
value={skipDocumentSelectorIfSingle}
onValueChange={setSkipDocumentSelectorIfSingle}
trackColor={{ false: slate200, true: blue600 }}
thumbColor={white}
disabled={skipDocumentSelector}
testID="skip-document-selector-if-single-toggle"
/>
</View>

{skipDocumentSelector && (
<Text style={styles.infoText}>
Document selection is always skipped. The &quot;Skip when only one
document&quot; setting has no effect.
</Text>
)}
</YStack>
</ScrollView>
</YStack>
);
};

const styles = StyleSheet.create({
sectionTitle: {
fontSize: 14,
fontFamily: dinot,
fontWeight: '600',
color: slate500,
textTransform: 'uppercase',
letterSpacing: 1,
},
settingRow: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
gap: 16,
},
settingTextContainer: {
flex: 1,
gap: 4,
},
settingLabel: {
fontSize: 16,
fontFamily: dinot,
fontWeight: '500',
color: black,
},
settingDescription: {
fontSize: 14,
fontFamily: dinot,
color: slate500,
},
divider: {
height: 1,
backgroundColor: slate200,
},
infoText: {
fontSize: 13,
fontFamily: dinot,
fontStyle: 'italic',
color: slate500,
paddingHorizontal: 4,
},
});

export { ProofSettingsScreen };
4 changes: 3 additions & 1 deletion app/src/screens/account/settings/SettingsScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type { SvgProps } from 'react-native-svg';
import { Button, ScrollView, View, XStack, YStack } from 'tamagui';
import { useFocusEffect, useNavigation } from '@react-navigation/native';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { Bug, FileText } from '@tamagui/lucide-icons';
import { Bug, FileText, Settings2 } from '@tamagui/lucide-icons';

import { BodyText, pressedStyle } from '@selfxyz/mobile-sdk-alpha/components';
import {
Expand Down Expand Up @@ -78,6 +78,7 @@ const routes =
[Data, 'View document info', 'DocumentDataInfo'],
[Lock, 'Reveal recovery phrase', 'ShowRecoveryPhrase'],
[Cloud, 'Cloud backup', 'CloudBackupSettings'],
[Settings2 as React.FC<SvgProps>, 'Proof settings', 'ProofSettings'],
[Feedback, 'Send feedback', 'email_feedback'],
[ShareIcon, 'Share Self app', 'share'],
[
Expand All @@ -88,6 +89,7 @@ const routes =
] satisfies [React.FC<SvgProps>, string, RouteOption][])
: ([
[Data, 'View document info', 'DocumentDataInfo'],
[Settings2 as React.FC<SvgProps>, 'Proof settings', 'ProofSettings'],
[Feedback, 'Send feeback', 'email_feedback'],
[
FileText as React.FC<SvgProps>,
Expand Down
5 changes: 4 additions & 1 deletion app/src/screens/dev/DevSettingsScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,10 @@ const ScreenSelector = ({}) => {
/>
</Button>
</XStack>
<ScrollView showsVerticalScrollIndicator={false}>
<ScrollView
showsVerticalScrollIndicator={false}
contentContainerStyle={{ paddingBottom: 100 }}
>
{screenList.map(item => (
<TouchableOpacity
key={item}
Expand Down
Loading
Loading