diff --git a/native/account/qr-auth-progress-screen.react.js b/native/account/qr-auth-progress-screen.react.js new file mode 100644 --- /dev/null +++ b/native/account/qr-auth-progress-screen.react.js @@ -0,0 +1,206 @@ +// @flow + +import { useFocusEffect } from '@react-navigation/core'; +import * as React from 'react'; +import { View, Text } from 'react-native'; +import * as Progress from 'react-native-progress'; + +import { useSecondaryDeviceQRAuthContext } from 'lib/components/secondary-device-qr-auth-context-provider.react.js'; + +import AuthContainer from './auth-components/auth-container.react.js'; +import AuthContentContainer from './auth-components/auth-content-container.react.js'; +import type { AuthNavigationProp } from './registration/auth-navigator.react.js'; +import { + RestoreBackupErrorScreenRouteName, + type NavigationRoute, +} from '../navigation/route-names.js'; +import { useSelector } from '../redux/redux-utils.js'; +import { useColors, useStyles } from '../themes/colors.js'; + +type ProgressStepProps = { + +stepNumber: string, + +state: 'pending' | 'active' | 'completed', + +label: string, +}; + +function ProgressStep(props: ProgressStepProps): React.Node { + const { stepNumber, state, label } = props; + const styles = useStyles(unboundStyles); + // const colors = useColors(); + + const stepStyle = React.useMemo(() => { + switch (state) { + case 'completed': + return [styles.stepIcon, styles.stepIconCompleted]; + case 'active': + return [styles.stepIcon, styles.stepIconActive]; + default: + return [styles.stepIcon, styles.stepIconPending]; + } + }, [state, styles]); + + const stepContent = state === 'completed' ? '✓' : stepNumber; + + return ( + + + {stepContent} + + + {label} + + + ); +} + +type Props = { + +navigation: AuthNavigationProp<'QRAuthProgressScreen'>, + +route: NavigationRoute<'QRAuthProgressScreen'>, +}; + +function QRAuthProgressScreen(props: Props): React.Node { + const styles = useStyles(unboundStyles); + const colors = useColors(); + + const { qrAuthInProgress } = useSecondaryDeviceQRAuthContext(); + const userDataRestoreStatus = useSelector( + state => state.restoreBackupState.status, + ); + + useFocusEffect( + React.useCallback(() => { + if (userDataRestoreStatus === 'user_data_restore_failed') { + props.navigation.navigate(RestoreBackupErrorScreenRouteName, { + deviceType: 'secondary', + }); + } + }, [props.navigation, userDataRestoreStatus]), + ); + + const userDataRestoreStarted = userDataRestoreStatus !== 'no_backup'; + const step: 'authenticating' | 'restoring' = + qrAuthInProgress && !userDataRestoreStarted + ? 'authenticating' + : 'restoring'; + + const authStepState = step === 'authenticating' ? 'active' : 'completed'; + const restoringStepState = step === 'restoring' ? 'active' : 'pending'; + const title = + step === 'authenticating' ? 'Authenticating device' : 'Restoring your data'; + + return ( + + + {title}... + + + + + + + + + + You will be automatically navigated to the app after this process is + finished. + + + + + + + ); +} + +const unboundStyles = { + container: { + flex: 1, + }, + title: { + fontSize: 24, + color: 'panelForegroundLabel', + paddingBottom: 40, + }, + section: { + fontFamily: 'Arial', + fontSize: 15, + lineHeight: 20, + color: 'panelForegroundSecondaryLabel', + paddingBottom: 16, + }, + progressContainer: { + flexGrow: 1, + alignItems: 'center', + justifyContent: 'center', + }, + progressSteps: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + marginBottom: 24, + }, + stepContainer: { + alignItems: 'center', + justifyContent: 'center', + }, + stepIcon: { + width: 40, + height: 40, + borderRadius: 20, + alignItems: 'center', + justifyContent: 'center', + marginBottom: 4, + }, + stepIconPending: { + backgroundColor: 'transparent', + borderWidth: 2, + borderColor: 'panelSecondaryForegroundBorder', + }, + stepIconActive: { + backgroundColor: 'purpleButton', + borderWidth: 2, + borderColor: 'violetLight100', + }, + stepIconCompleted: { + backgroundColor: 'greenIndicatorInner', + borderWidth: 2, + borderColor: 'greenIndicatorOuter', + }, + stepIconText: { + color: 'panelForegroundIcon', + fontSize: 16, + fontWeight: 'bold', + }, + stepLabelContainer: { + maxWidth: 100, + height: 48, + justifyContent: 'center', + }, + stepLabel: { + color: 'panelForegroundLabel', + fontSize: 14, + textAlign: 'center', + }, + stepConnector: { + width: 30, + height: 2, + backgroundColor: 'panelSecondaryForegroundBorder', + marginHorizontal: 10, + marginBottom: 48, + }, +}; + +export default QRAuthProgressScreen; diff --git a/native/account/qr-code-screen.react.js b/native/account/qr-code-screen.react.js --- a/native/account/qr-code-screen.react.js +++ b/native/account/qr-code-screen.react.js @@ -22,7 +22,10 @@ import type { AuthNavigationProp } from './registration/auth-navigator.react.js'; import LinkButton from '../components/link-button.react.js'; import type { NavigationRoute } from '../navigation/route-names.js'; -import { RestorePromptScreenRouteName } from '../navigation/route-names.js'; +import { + RestorePromptScreenRouteName, + QRAuthProgressScreenRouteName, +} from '../navigation/route-names.js'; import { useColors, useStyles } from '../themes/colors.js'; type QRCodeScreenProps = { @@ -31,8 +34,13 @@ }; function QRCodeScreen(props: QRCodeScreenProps): React.Node { - const { qrData, openSecondaryQRAuth, closeSecondaryQRAuth, canGenerateQRs } = - useSecondaryDeviceQRAuthContext(); + const { + qrData, + openSecondaryQRAuth, + closeSecondaryQRAuth, + canGenerateQRs, + qrAuthInProgress, + } = useSecondaryDeviceQRAuthContext(); const [attemptNumber, setAttemptNumber] = React.useState(0); @@ -59,6 +67,14 @@ }, [closeSecondaryQRAuth]), ); + useFocusEffect( + React.useCallback(() => { + if (qrAuthInProgress) { + props.navigation.navigate(QRAuthProgressScreenRouteName); + } + }, [qrAuthInProgress, props.navigation]), + ); + const { platform } = getConfig().platformDetails; const qrCodeURL = React.useMemo(() => { if (!qrData || !canGenerateQRs) { diff --git a/native/account/registration/auth-navigator.react.js b/native/account/registration/auth-navigator.react.js --- a/native/account/registration/auth-navigator.react.js +++ b/native/account/registration/auth-navigator.react.js @@ -55,6 +55,7 @@ type ScreenParamList, type AuthParamList, QRCodeScreenRouteName, + QRAuthProgressScreenRouteName, RestorePromptScreenRouteName, RestorePasswordAccountScreenRouteName, RestoreBackupScreenRouteName, @@ -62,6 +63,7 @@ RestoreSIWEBackupRouteName, ConnectFarcasterDCsRouteName, } from '../../navigation/route-names.js'; +import QRAuthProgressScreen from '../qr-auth-progress-screen.react.js'; import QRCodeScreen from '../qr-code-screen.react.js'; import RestoreBackupErrorScreen from '../restore-backup-error-screen.react.js'; import RestoreBackupScreen from '../restore-backup-screen.react.js'; @@ -214,6 +216,11 @@ component={AccountDoesNotExist} /> +