Skip to content
3 changes: 3 additions & 0 deletions app/src/components/Mnemonic.tsx
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 Clipboard from '@react-native-clipboard/clipboard';

Check warning on line 3 in app/src/components/Mnemonic.tsx

View workflow job for this annotation

GitHub Actions / lint

Run autofix to sort these imports!

Check warning on line 3 in app/src/components/Mnemonic.tsx

View workflow job for this annotation

GitHub Actions / lint

Run autofix to sort these imports!
import React, { useCallback, useState } from 'react';
import { Button, Text, XStack, YStack } from 'tamagui';

Expand All @@ -14,6 +14,7 @@
white,
} from '../utils/colors';
import { confirmTap } from '../utils/haptic';
import { useSettingStore } from '../stores/settingStore';

interface MnemonicProps {
words?: string[];
Expand Down Expand Up @@ -50,11 +51,13 @@
const Mnemonic = ({ words = REDACTED, onRevealWords }: MnemonicProps) => {
const [revealWords, setRevealWords] = useState(false);
const [copied, setCopied] = useState(false);
const { setHasViewedRecoveryPhrase } = useSettingStore();
const copyToClipboardOrReveal = useCallback(async () => {
confirmTap();
if (!revealWords) {
// TODO: container jumps when words are revealed on android
await onRevealWords?.();
setHasViewedRecoveryPhrase(true);
return setRevealWords(previous => !previous);
}
Clipboard.setString(words.join(' '));
Expand Down
40 changes: 40 additions & 0 deletions app/src/hooks/useRecoveryPrompts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { useEffect } from 'react';

Check warning on line 1 in app/src/hooks/useRecoveryPrompts.ts

View workflow job for this annotation

GitHub Actions / lint

Run autofix to sort these imports!

Check failure on line 1 in app/src/hooks/useRecoveryPrompts.ts

View workflow job for this annotation

GitHub Actions / lint

missing header

Check warning on line 1 in app/src/hooks/useRecoveryPrompts.ts

View workflow job for this annotation

GitHub Actions / lint

Run autofix to sort these imports!

Check failure on line 1 in app/src/hooks/useRecoveryPrompts.ts

View workflow job for this annotation

GitHub Actions / lint

missing header
import { navigationRef } from '../navigation';
import { useModal } from './useModal';
import { useSettingStore } from '../stores/settingStore';

const modalParams = {
titleText: 'Protect your account',
bodyText:
'Enable cloud backup or save your recovery phrase so you can recover your account.',
buttonText: 'Back up now',
onButtonPress: async () => {
if (navigationRef.isReady()) {
navigationRef.navigate('CloudBackupSettings', {
nextScreen: 'SaveRecoveryPhrase',
});
}
},
onModalDismiss: () => {},
} as const;

export default function useRecoveryPrompts() {
const { loginCount, cloudBackupEnabled, hasViewedRecoveryPhrase } =
useSettingStore();
const { showModal, visible } = useModal(modalParams);

useEffect(() => {
if (!navigationRef.isReady()) {
return;
}
if (!cloudBackupEnabled && !hasViewedRecoveryPhrase) {
const shouldPrompt =
loginCount > 0 && (loginCount <= 3 || (loginCount - 3) % 5 === 0);
if (shouldPrompt && !visible) {
showModal();
}
}
}, [loginCount, cloudBackupEnabled, hasViewedRecoveryPhrase, visible, showModal]);

Check warning on line 37 in app/src/hooks/useRecoveryPrompts.ts

View workflow job for this annotation

GitHub Actions / lint

Replace `loginCount,·cloudBackupEnabled,·hasViewedRecoveryPhrase,·visible,·showModal` with `⏎····loginCount,⏎····cloudBackupEnabled,⏎····hasViewedRecoveryPhrase,⏎····visible,⏎····showModal,⏎··`

Check warning on line 37 in app/src/hooks/useRecoveryPrompts.ts

View workflow job for this annotation

GitHub Actions / lint

Replace `loginCount,·cloudBackupEnabled,·hasViewedRecoveryPhrase,·visible,·showModal` with `⏎····loginCount,⏎····cloudBackupEnabled,⏎····hasViewedRecoveryPhrase,⏎····visible,⏎····showModal,⏎··`

return { visible };
}
2 changes: 2 additions & 0 deletions app/src/providers/authProvider.tsx
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 { ethers } from 'ethers';

Check warning on line 3 in app/src/providers/authProvider.tsx

View workflow job for this annotation

GitHub Actions / lint

Run autofix to sort these imports!

Check warning on line 3 in app/src/providers/authProvider.tsx

View workflow job for this annotation

GitHub Actions / lint

Run autofix to sort these imports!
import React, {
createContext,
PropsWithChildren,
Expand All @@ -15,6 +15,7 @@
import { AuthEvents } from '../consts/analytics';
import { Mnemonic } from '../types/mnemonic';
import analytics from '../utils/analytics';
import { useSettingStore } from '../stores/settingStore';

const { trackEvent } = analytics();

Expand Down Expand Up @@ -208,6 +209,7 @@

setIsAuthenticatingPromise(null);
setIsAuthenticated(true);
useSettingStore.getState().incrementLoginCount();
trackEvent(AuthEvents.BIOMETRIC_LOGIN_SUCCESS);
setAuthenticatedTimeout(previousTimeout => {
if (previousTimeout) {
Expand Down
2 changes: 2 additions & 0 deletions app/src/screens/home/HomeScreen.tsx
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 { useFocusEffect, usePreventRemove } from '@react-navigation/native';

Check warning on line 3 in app/src/screens/home/HomeScreen.tsx

View workflow job for this annotation

GitHub Actions / lint

Run autofix to sort these imports!

Check warning on line 3 in app/src/screens/home/HomeScreen.tsx

View workflow job for this annotation

GitHub Actions / lint

Run autofix to sort these imports!
import React from 'react';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { Button, styled, YStack } from 'tamagui';
Expand All @@ -10,6 +10,7 @@
import { Caption } from '../../components/typography/Caption';
import { useAppUpdates } from '../../hooks/useAppUpdates';
import useConnectionModal from '../../hooks/useConnectionModal';
import useRecoveryPrompts from '../../hooks/useRecoveryPrompts';
import useHapticNavigation from '../../hooks/useHapticNavigation';
import SelfCard from '../../images/card-style-1.svg';
import ScanIcon from '../../images/icons/qr_scan.svg';
Expand All @@ -36,6 +37,7 @@

const HomeScreen: React.FC = () => {
useConnectionModal();
useRecoveryPrompts();
const [isNewVersionAvailable, showAppUpdateModal, isModalDismissed] =
useAppUpdates();

Expand Down
17 changes: 17 additions & 0 deletions app/src/stores/settingStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
setBiometricsAvailable: (biometricsAvailable: boolean) => void;
cloudBackupEnabled: boolean;
toggleCloudBackupEnabled: () => void;
loginCount: number;
incrementLoginCount: () => void;
hasViewedRecoveryPhrase: boolean;
setHasViewedRecoveryPhrase: (viewed: boolean) => void;
isDevMode: boolean;
setDevModeOn: () => void;
setDevModeOff: () => void;
Expand Down Expand Up @@ -43,6 +47,19 @@
toggleCloudBackupEnabled: () =>
set(oldState => ({
cloudBackupEnabled: !oldState.cloudBackupEnabled,
loginCount: oldState.cloudBackupEnabled

Check warning on line 50 in app/src/stores/settingStore.ts

View workflow job for this annotation

GitHub Actions / lint

Replace `⏎············?·oldState.loginCount⏎···········` with `·?·oldState.loginCount`

Check warning on line 50 in app/src/stores/settingStore.ts

View workflow job for this annotation

GitHub Actions / lint

Replace `⏎············?·oldState.loginCount⏎···········` with `·?·oldState.loginCount`
? oldState.loginCount
: 0,
})),

loginCount: 0,
incrementLoginCount: () =>
set(oldState => ({ loginCount: oldState.loginCount + 1 })),
hasViewedRecoveryPhrase: false,
setHasViewedRecoveryPhrase: viewed =>
set(oldState => ({
hasViewedRecoveryPhrase: viewed,
loginCount: viewed ? 0 : oldState.loginCount,
})),

isDevMode: false,
Expand Down
Loading