diff --git a/suite-native/analytics/src/events.ts b/suite-native/analytics/src/events.ts index 12aa8c0cf1f..2070c5521f1 100644 --- a/suite-native/analytics/src/events.ts +++ b/suite-native/analytics/src/events.ts @@ -266,6 +266,7 @@ export type SuiteNativeAnalyticsEvent = symbol: NetworkSymbol; outputsCount: number; selectedFee: FeeLevelLabel; + wasAppLeftDuringReview: boolean; tokenSymbols?: TokenSymbol[]; tokenAddresses?: TokenAddress[]; hasEthereumData?: boolean; diff --git a/suite-native/module-send/package.json b/suite-native/module-send/package.json index 2c518606a5a..e8734368a0e 100644 --- a/suite-native/module-send/package.json +++ b/suite-native/module-send/package.json @@ -44,6 +44,7 @@ "@trezor/transport": "workspace:*", "@trezor/utils": "workspace:*", "expo-linear-gradient": "13.0.2", + "jotai": "1.9.1", "react": "18.2.0", "react-hook-form": "^7.53.0", "react-native": "0.75.2", diff --git a/suite-native/module-send/src/atoms/wasAppLeftDuringReviewAtom.ts b/suite-native/module-send/src/atoms/wasAppLeftDuringReviewAtom.ts new file mode 100644 index 00000000000..492b3cfe4f0 --- /dev/null +++ b/suite-native/module-send/src/atoms/wasAppLeftDuringReviewAtom.ts @@ -0,0 +1,3 @@ +import { atom } from 'jotai'; + +export const wasAppLeftDuringReviewAtom = atom(false); diff --git a/suite-native/module-send/src/components/AddressReviewStepList.tsx b/suite-native/module-send/src/components/AddressReviewStepList.tsx index a2ad4d1fd61..2e9c96f2b11 100644 --- a/suite-native/module-send/src/components/AddressReviewStepList.tsx +++ b/suite-native/module-send/src/components/AddressReviewStepList.tsx @@ -1,9 +1,10 @@ +import { useCallback, useEffect, useState } from 'react'; +import { LayoutChangeEvent, View, AppState } from 'react-native'; import { useDispatch, useSelector } from 'react-redux'; -import { useEffect, useState } from 'react'; -import { LayoutChangeEvent, View } from 'react-native'; +import { useSetAtom } from 'jotai'; import { isRejected } from '@reduxjs/toolkit'; -import { useNavigation, useRoute } from '@react-navigation/native'; +import { useNavigation, useRoute, useFocusEffect } from '@react-navigation/native'; import { RootStackParamList, @@ -27,6 +28,7 @@ import { AddressReviewStep } from '../components/AddressReviewStep'; import { CompareAddressHelpButton } from '../components/CompareAddressHelpButton'; import { AddressOriginHelpButton } from '../components/AddressOriginHelpButton'; import { useHandleSendReviewFailure } from '../hooks/useHandleSendReviewFailure'; +import { wasAppLeftDuringReviewAtom } from '../atoms/wasAppLeftDuringReviewAtom'; const NUMBER_OF_STEPS = 3; const OVERLAY_INITIAL_POSITION = 75; @@ -48,6 +50,23 @@ export const AddressReviewStepList = () => { const [childHeights, setChildHeights] = useState([]); const [stepIndex, setStepIndex] = useState(0); const handleSendReviewFailure = useHandleSendReviewFailure({ accountKey, transaction }); + const setWasAppLeftDuringReview = useSetAtom(wasAppLeftDuringReviewAtom); + + useFocusEffect( + useCallback(() => { + setWasAppLeftDuringReview(false); + + const subscription = AppState.addEventListener('change', nextAppState => { + if (nextAppState === 'background') { + setWasAppLeftDuringReview(true); + } + }); + + return () => { + subscription.remove(); + }; + }, [setWasAppLeftDuringReview]), + ); const areAllStepsDone = stepIndex === NUMBER_OF_STEPS - 1; const isLayoutReady = childHeights.length === NUMBER_OF_STEPS; diff --git a/suite-native/module-send/src/components/OutputsReviewFooter.tsx b/suite-native/module-send/src/components/OutputsReviewFooter.tsx index 9f97f7e7a1d..697118e1468 100644 --- a/suite-native/module-send/src/components/OutputsReviewFooter.tsx +++ b/suite-native/module-send/src/components/OutputsReviewFooter.tsx @@ -1,7 +1,8 @@ +import { useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import Animated, { SlideInDown } from 'react-native-reanimated'; -import { useState } from 'react'; +import { useAtomValue } from 'jotai'; import { CommonActions, useNavigation } from '@react-navigation/native'; import { isFulfilled } from '@reduxjs/toolkit'; @@ -21,6 +22,7 @@ import { analytics, EventType } from '@suite-native/analytics'; import { SendConfirmOnDeviceImage } from '../components/SendConfirmOnDeviceImage'; import { sendTransactionAndCleanupSendFormThunk } from '../sendFormThunks'; +import { wasAppLeftDuringReviewAtom } from '../atoms/wasAppLeftDuringReviewAtom'; const navigateToAccountDetail = ({ accountKey, @@ -64,6 +66,7 @@ export const OutputsReviewFooter = ({ accountKey }: { accountKey: AccountKey }) const navigation = useNavigation(); const { applyStyle } = useNativeStyles(); const [isSendInProgress, setIsSendInProgress] = useState(false); + const wasAppLeftDuringReview = useAtomValue(wasAppLeftDuringReviewAtom); const account = useSelector((state: AccountsRootState) => selectAccountByKey(state, accountKey), @@ -94,6 +97,7 @@ export const OutputsReviewFooter = ({ accountKey }: { accountKey: AccountKey }) symbol: account.symbol, outputsCount: formValues.outputs.length, selectedFee: formValues.selectedFee ?? 'normal', + wasAppLeftDuringReview, }, }); } diff --git a/yarn.lock b/yarn.lock index 4e061f831af..badb9c5bc1d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10368,6 +10368,7 @@ __metadata: "@trezor/transport": "workspace:*" "@trezor/utils": "workspace:*" expo-linear-gradient: "npm:13.0.2" + jotai: "npm:1.9.1" react: "npm:18.2.0" react-hook-form: "npm:^7.53.0" react-native: "npm:0.75.2"