Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions app/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1428,8 +1428,6 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- react-native-safe-area-context (5.4.1):
- React-Core
- react-native-sqlite-storage (6.0.1):
- React-Core
- React-nativeconfig (0.75.4)
Expand Down Expand Up @@ -1869,7 +1867,6 @@ DEPENDENCIES:
- "react-native-netinfo (from `../../node_modules/@react-native-community/netinfo`)"
- react-native-nfc-manager (from `../../node_modules/react-native-nfc-manager`)
- react-native-quick-crypto (from `../../node_modules/react-native-quick-crypto`)
- react-native-safe-area-context (from `../../node_modules/react-native-safe-area-context`)
- react-native-sqlite-storage (from `../../node_modules/react-native-sqlite-storage`)
- React-nativeconfig (from `../../node_modules/react-native/ReactCommon`)
- React-NativeModulesApple (from `../../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`)
Expand Down Expand Up @@ -2028,8 +2025,6 @@ EXTERNAL SOURCES:
:path: "../../node_modules/react-native-nfc-manager"
react-native-quick-crypto:
:path: "../../node_modules/react-native-quick-crypto"
react-native-safe-area-context:
:path: "../../node_modules/react-native-safe-area-context"
react-native-sqlite-storage:
:path: "../../node_modules/react-native-sqlite-storage"
React-nativeconfig:
Expand Down Expand Up @@ -2185,7 +2180,6 @@ SPEC CHECKSUMS:
react-native-netinfo: cec9c4e86083cb5b6aba0e0711f563e2fbbff187
react-native-nfc-manager: a280ef94cd4871a471b052f0dc70381cf1223049
react-native-quick-crypto: c42da02f2832626ec0b8d8465f2f9b3889a317f6
react-native-safe-area-context: dde2052b903c11d677c320b599c3244021c34ce8
react-native-sqlite-storage: 0c84826214baaa498796c7e46a5ccc9a82e114ed
React-nativeconfig: 31072ab0146e643594f6959c7f970a04b6c9ddd0
React-NativeModulesApple: 4ffcab4cdf34002540799bffbedd6466e8023c3a
Expand Down
2 changes: 1 addition & 1 deletion app/jest.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module.exports = {
preset: 'react-native',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
transformIgnorePatterns: [
'node_modules/(?!(react-native|@react-native|@react-navigation|@react-native-community|@segment/analytics-react-native|@openpassport|react-native-keychain|react-native-check-version|react-native-nfc-manager|react-native-passport-reader|react-native-gesture-handler|uuid|@stablelib|@react-native-google-signin|react-native-cloud-storage|@react-native-clipboard|@react-native-firebase|@sentry)/)',
'node_modules/(?!(react-native|@react-native|@react-navigation|@react-native-community|@segment/analytics-react-native|@openpassport|react-native-keychain|react-native-check-version|react-native-nfc-manager|react-native-passport-reader|react-native-gesture-handler|uuid|@stablelib|@react-native-google-signin|react-native-cloud-storage|@react-native-clipboard|@react-native-firebase)/)',
],
setupFiles: ['<rootDir>/jest.setup.js'],
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.[jt]sx?$',
Expand Down
1 change: 0 additions & 1 deletion app/src/consts/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ export const ProofEvents = {
PROOF_VERIFICATION_STARTED: 'Proof: Proof Verification Started',
PROVING_PROCESS_ERROR: 'Proof: Proving Process Error',
PROVING_STATE_CHANGE: 'Proof: Proving State Change',
PROVING_STORE_REINITIALIZED: 'Proof: Proving Store Re-initialized',
REGISTER_COMPLETED: 'Proof: Register Completed',
ALREADY_REGISTERED: 'Proof: Already Registered',
QR_SCAN_CANCELLED: 'Proof: QR Scan Cancelled',
Expand Down
4 changes: 1 addition & 3 deletions app/src/hooks/useRecoveryPrompts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,7 @@ export default function useRecoveryPrompts() {
}
}
}
maybePrompt().catch(() => {
// Silently fail to avoid breaking the hook
});
maybePrompt().catch(() => {});
}, [
loginCount,
cloudBackupEnabled,
Expand Down
11 changes: 1 addition & 10 deletions app/src/screens/misc/LoadingScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,19 +144,10 @@ const LoadingScreen: React.FC<LoadingScreenProps> = ({}) => {

// Update UI if passport data is available
if (passportData?.passportMetadata) {
// Get the current circuit type from the proving store
const circuitType = useProvingStore.getState().circuitType;

// Determine the appropriate type for loading screen text
// 'disclose' circuit type should use 'register' timing, but 'dsc' should use 'dsc' timing
const loadingScreenType: 'dsc' | 'register' =
circuitType === 'dsc' ? 'dsc' : 'register';

// Update loading text based on current state and circuit type
// Update loading text based on current state
const { actionText, estimatedTime } = getLoadingScreenText(
currentState as ProvingStateType,
passportData?.passportMetadata,
loadingScreenType,
);
setLoadingText({ actionText, estimatedTime });

Expand Down
28 changes: 0 additions & 28 deletions app/src/screens/prove/ConfirmBelongingScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { Title } from '../../components/typography/Title';
import { PassportEvents, ProofEvents } from '../../consts/analytics';
import useHapticNavigation from '../../hooks/useHapticNavigation';
import { ExpandableBottomLayout } from '../../layouts/ExpandableBottomLayout';
import { captureException } from '../../Sentry';
import analytics from '../../utils/analytics';
import { black, white } from '../../utils/colors';
import { notificationSuccess } from '../../utils/haptic';
Expand Down Expand Up @@ -60,33 +59,6 @@ const ConfirmBelongingScreen: React.FC<ConfirmBelongingScreenProps> = ({}) => {
// Mark as user confirmed - proving will start automatically when ready
provingStore.setUserConfirmed();

// Ensure proving store is initialized before navigation
if (provingStore.circuitType !== 'dsc') {
try {
console.error(
'Re-initializing proving store with DSC circuit type before navigation',
);
trackEvent(ProofEvents.PROVING_STORE_REINITIALIZED, {
reason: 'circuit_type_mismatch',
expected_type: 'dsc',
current_type: provingStore.circuitType,
});
await provingStore.init('dsc', true);
} catch (error: any) {
console.error('Error during proving store re-initialization:', error);
captureException(error, {
context: 'proving_store_reinitialization',
circuit_type: 'dsc',
current_circuit_type: provingStore.circuitType,
});
trackEvent(ProofEvents.PROVING_PROCESS_ERROR, {
error: error?.message || 'Unknown re-initialization error',
context: 'proving_store_reinitialization',
});
throw error; // Re-throw to be handled by the outer try-catch
}
}

// Navigate to loading screen
navigate();
} catch (error: any) {
Expand Down
2 changes: 0 additions & 2 deletions app/src/screens/recovery/RecoverWithPhraseScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import {
loadPassportDataAndSecret,
reStorePassportDataWithRightCSCA,
} from '../../providers/passportDataProvider';
import { useSettingStore } from '../../stores/settingStore';
import analytics from '../../utils/analytics';
import {
black,
Expand Down Expand Up @@ -82,7 +81,6 @@ const RecoverWithPhraseScreen: React.FC<

setRestoring(false);
trackEvent(BackupEvents.ACCOUNT_RECOVERY_COMPLETED);
useSettingStore.getState().setHasViewedRecoveryPhrase(true);
navigation.navigate('AccountVerifiedSuccess');
}, [mnemonic, restoreAccountFromMnemonic]);

Expand Down
26 changes: 7 additions & 19 deletions app/src/screens/recovery/SaveRecoveryPhraseScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// SPDX-License-Identifier: BUSL-1.1; Copyright (c) 2025 Social Connect Labs, Inc.; Licensed under BUSL-1.1 (see LICENSE); Apache-2.0 from 2029-06-11

import { StaticScreenProps } from '@react-navigation/native';
import React, { useCallback, useState } from 'react';

import { PrimaryButton } from '../../components/buttons/PrimaryButton';
Expand All @@ -12,19 +11,15 @@ import { Title } from '../../components/typography/Title';
import useHapticNavigation from '../../hooks/useHapticNavigation';
import useMnemonic from '../../hooks/useMnemonic';
import { ExpandableBottomLayout } from '../../layouts/ExpandableBottomLayout';
import { RootStackParamList } from '../../navigation';
import { useSettingStore } from '../../stores/settingStore';
import { STORAGE_NAME } from '../../utils/cloudBackup';
import { black, slate400, white } from '../../utils/colors';
import { useProvingStore } from '../../utils/proving/provingMachine';

type NextScreen = keyof RootStackParamList;
interface SaveRecoveryPhraseScreenProps
extends StaticScreenProps<{ nextScreen?: NextScreen } | undefined> {}
interface SaveRecoveryPhraseScreenProps {}

const SaveRecoveryPhraseScreen: React.FC<SaveRecoveryPhraseScreenProps> = ({
route,
}) => {
const SaveRecoveryPhraseScreen: React.FC<
SaveRecoveryPhraseScreenProps
> = ({}) => {
Comment on lines +20 to +22
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix empty object destructuring pattern.

The static analysis tool correctly flagged the empty object pattern ({}) in the destructuring. Since this component doesn't accept any props, remove the destructuring entirely.

Apply this fix:

-const SaveRecoveryPhraseScreen: React.FC<
-  SaveRecoveryPhraseScreenProps
-> = ({}) => {
+const SaveRecoveryPhraseScreen: React.FC<
+  SaveRecoveryPhraseScreenProps
+> = () => {
📝 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 SaveRecoveryPhraseScreen: React.FC<
SaveRecoveryPhraseScreenProps
> = ({}) => {
const SaveRecoveryPhraseScreen: React.FC<
SaveRecoveryPhraseScreenProps
> = () => {
🧰 Tools
🪛 Biome (1.9.4)

[error] 22-22: Unexpected empty object pattern.

(lint/correctness/noEmptyPattern)

🤖 Prompt for AI Agents
In app/src/screens/recovery/SaveRecoveryPhraseScreen.tsx around lines 20 to 22,
the component uses an empty object destructuring pattern `({})` which is
unnecessary since no props are accepted. Remove the empty destructuring from the
function parameter list so the component is defined without any parameters.

const [userHasSeenMnemonic, setUserHasSeenMnemonic] = useState(false);
const { mnemonic, loadMnemonic } = useMnemonic();
const { cloudBackupEnabled } = useSettingStore();
Expand All @@ -34,19 +29,12 @@ const SaveRecoveryPhraseScreen: React.FC<SaveRecoveryPhraseScreenProps> = ({
setUserHasSeenMnemonic(true);
}, []);

const nextScreen = route.params?.nextScreen ?? 'AccountVerifiedSuccess';

const onCloudBackupPress = useHapticNavigation('CloudBackupSettings', {
params: { nextScreen: 'SaveRecoveryPhrase' },
});

const navigateNext = useHapticNavigation(nextScreen, { action: 'confirm' });
const onSkipPress = useCallback(() => {
if (nextScreen === 'LoadingScreen') {
useProvingStore.getState().init('register', true);
}
navigateNext();
}, [navigateNext, nextScreen]);
const onSkipPress = useHapticNavigation('AccountVerifiedSuccess', {
action: 'confirm',
});

return (
<ExpandableBottomLayout.Layout backgroundColor={black}>
Expand Down
22 changes: 5 additions & 17 deletions app/src/utils/proving/provingMachine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,8 @@ import {
const { trackEvent } = analytics();

export const getPostVerificationRoute = () => {
const { cloudBackupEnabled, hasViewedRecoveryPhrase } =
useSettingStore.getState();
return cloudBackupEnabled || hasViewedRecoveryPhrase
? 'AccountVerifiedSuccess'
: 'SaveRecoveryPhrase';
const { cloudBackupEnabled } = useSettingStore.getState();
return cloudBackupEnabled ? 'AccountVerifiedSuccess' : 'SaveRecoveryPhrase';
};

const provingMachine = createMachine({
Expand Down Expand Up @@ -765,18 +762,9 @@ export const useProvingStore = create<ProvingState>((set, get) => {
_checkActorInitialized(actor);
const { circuitType } = get();
if (circuitType === 'dsc') {
const { hasViewedRecoveryPhrase } = useSettingStore.getState();
if (!hasViewedRecoveryPhrase) {
if (navigationRef.isReady()) {
navigationRef.navigate('SaveRecoveryPhrase', {
nextScreen: 'LoadingScreen',
});
}
} else {
setTimeout(() => {
get().init('register', true);
}, 3000);
}
setTimeout(() => {
get().init('register', true);
}, 1500);
} else if (circuitType === 'register') {
actor!.send({ type: 'COMPLETED' });
} else if (circuitType === 'disclose') {
Expand Down
47 changes: 13 additions & 34 deletions app/tests/src/hooks/useRecoveryPrompts.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: BUSL-1.1; Copyright (c) 2025 Social Connect Labs, Inc.; Licensed under BUSL-1.1 (see LICENSE); Apache-2.0 from 2029-06-11

import { act, renderHook, waitFor } from '@testing-library/react-native';
import { renderHook, waitFor } from '@testing-library/react-native';

import { useModal } from '../../../src/hooks/useModal';
import useRecoveryPrompts from '../../../src/hooks/useRecoveryPrompts';
Expand All @@ -25,49 +25,39 @@ describe('useRecoveryPrompts', () => {
beforeEach(() => {
showModal.mockClear();
getAllDocuments.mockResolvedValue({ doc1: {} as any });
act(() => {
useSettingStore.setState({
loginCount: 0,
cloudBackupEnabled: false,
hasViewedRecoveryPhrase: false,
});
useSettingStore.setState({
loginCount: 0,
cloudBackupEnabled: false,
hasViewedRecoveryPhrase: false,
});
});

it('shows modal on first login', async () => {
act(() => {
useSettingStore.setState({ loginCount: 1 });
});
useSettingStore.setState({ loginCount: 1 });
renderHook(() => useRecoveryPrompts());
await waitFor(() => {
expect(showModal).toHaveBeenCalled();
});
});

it('does not show modal when login count is 4', async () => {
act(() => {
useSettingStore.setState({ loginCount: 4 });
});
useSettingStore.setState({ loginCount: 4 });
renderHook(() => useRecoveryPrompts());
await waitFor(() => {
expect(showModal).not.toHaveBeenCalled();
});
});

it('shows modal on eighth login', async () => {
act(() => {
useSettingStore.setState({ loginCount: 8 });
});
useSettingStore.setState({ loginCount: 8 });
renderHook(() => useRecoveryPrompts());
await waitFor(() => {
expect(showModal).toHaveBeenCalled();
});
});

it('does not show modal if backup already enabled', async () => {
act(() => {
useSettingStore.setState({ loginCount: 1, cloudBackupEnabled: true });
});
useSettingStore.setState({ loginCount: 1, cloudBackupEnabled: true });
renderHook(() => useRecoveryPrompts());
await waitFor(() => {
expect(showModal).not.toHaveBeenCalled();
Expand All @@ -77,22 +67,15 @@ describe('useRecoveryPrompts', () => {
it('does not show modal when navigation is not ready', async () => {
const navigationRef = require('../../../src/navigation').navigationRef;
navigationRef.isReady.mockReturnValueOnce(false);
act(() => {
useSettingStore.setState({ loginCount: 1 });
});
useSettingStore.setState({ loginCount: 1 });
renderHook(() => useRecoveryPrompts());
await waitFor(() => {
expect(showModal).not.toHaveBeenCalled();
});
});

it('does not show modal when recovery phrase has been viewed', async () => {
act(() => {
useSettingStore.setState({
loginCount: 1,
hasViewedRecoveryPhrase: true,
});
});
useSettingStore.setState({ loginCount: 1, hasViewedRecoveryPhrase: true });
renderHook(() => useRecoveryPrompts());
await waitFor(() => {
expect(showModal).not.toHaveBeenCalled();
Expand All @@ -101,9 +84,7 @@ describe('useRecoveryPrompts', () => {

it('does not show modal when no documents exist', async () => {
getAllDocuments.mockResolvedValueOnce({});
act(() => {
useSettingStore.setState({ loginCount: 1 });
});
useSettingStore.setState({ loginCount: 1 });
renderHook(() => useRecoveryPrompts());
await waitFor(() => {
expect(showModal).not.toHaveBeenCalled();
Expand All @@ -113,9 +94,7 @@ describe('useRecoveryPrompts', () => {
it('shows modal for other valid login counts', async () => {
for (const count of [2, 3, 13, 18]) {
showModal.mockClear();
act(() => {
useSettingStore.setState({ loginCount: count });
});
useSettingStore.setState({ loginCount: count });
renderHook(() => useRecoveryPrompts());
await waitFor(() => {
expect(showModal).toHaveBeenCalled();
Expand Down
Loading
Loading