Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
40 changes: 4 additions & 36 deletions app/components/Nav/Main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,6 @@ import { useIdentityEffects } from '../../../util/identity/hooks/useIdentityEffe
import ProtectWalletMandatoryModal from '../../Views/ProtectWalletMandatoryModal/ProtectWalletMandatoryModal';
import { selectIsSeedlessPasswordOutdated } from '../../../selectors/seedlessOnboardingController';
import { Authentication } from '../../../core';
import { IconName } from '../../../component-library/components/Icons/Icon';
import Routes from '../../../constants/navigation/Routes';
import { useCompletedOnboardingEffect } from '../../../util/onboarding/hooks/useCompletedOnboardingEffect';
import {
useNetworksByNamespace,
Expand Down Expand Up @@ -128,40 +126,10 @@ const Main = (props) => {
);

useEffect(() => {
const checkIsSeedlessPasswordOutdated = async () => {
if (isSeedlessPasswordOutdated) {
// Check for latest seedless password outdated state
// isSeedlessPasswordOutdated is true when navigate to wallet main screen after login with password sync
const isOutdated =
await Authentication.checkIsSeedlessPasswordOutdated(false);
if (!isOutdated) {
return;
}

// show seedless password outdated modal and force user to lock app
props.navigation.navigate(Routes.MODAL.ROOT_MODAL_FLOW, {
screen: Routes.SHEET.SUCCESS_ERROR_SHEET,
params: {
title: strings('login.seedless_password_outdated_modal_title'),
description: strings(
'login.seedless_password_outdated_modal_content',
),
primaryButtonLabel: strings(
'login.seedless_password_outdated_modal_confirm',
),
type: 'error',
icon: IconName.Danger,
isInteractable: false,
onPrimaryButtonPress: async () => {
await Authentication.lockApp({ locked: true });
},
closeOnPrimaryButtonPress: true,
},
});
}
};
checkIsSeedlessPasswordOutdated();
}, [isSeedlessPasswordOutdated, props.navigation]);
Authentication.checkAndShowSeedlessPasswordOutdatedModal(
isSeedlessPasswordOutdated,
);
}, [isSeedlessPasswordOutdated]);

const { connectionChangeHandler } = useConnectionHandler(props.navigation);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,9 @@ const PerpsMarketDetailsView: React.FC<PerpsMarketDetailsViewProps> = () => {
const [selectedTooltip, setSelectedTooltip] =
useState<PerpsTooltipContentKey | null>(null);

// Stop loss prompt banner state
// Stop loss prompt banner state - for loading/success when setting stop loss via banner
const [isSettingStopLoss, setIsSettingStopLoss] = useState(false);
const [isStopLossSuccess, setIsStopLossSuccess] = useState(false);
const [hideBannerAfterSuccess, setHideBannerAfterSuccess] = useState(false);

const isEligible = useSelector(selectPerpsEligibility);

Expand Down Expand Up @@ -409,21 +408,23 @@ const PerpsMarketDetailsView: React.FC<PerpsMarketDetailsViewProps> = () => {
}, [existingPosition, currentPrice]);

// Stop loss prompt banner logic
// Hook handles visibility orchestration including fade-out animation
const {
shouldShowBanner,
variant: bannerVariant,
liquidationDistance,
suggestedStopLossPrice,
suggestedStopLossPercent,
isVisible: isBannerVisible,
isDismissing: isBannerDismissing,
onDismissComplete: handleBannerDismissComplete,
} = useStopLossPrompt({
position: existingPosition,
currentPrice,
positionOpenedTimestamp,
});

// Reset stop loss banner state when market or position changes
// Reset stop loss success state when market or position changes
useEffect(() => {
setHideBannerAfterSuccess(false);
setIsStopLossSuccess(false);
}, [market?.symbol, existingPosition?.coin]);

Expand Down Expand Up @@ -800,10 +801,11 @@ const PerpsMarketDetailsView: React.FC<PerpsMarketDetailsViewProps> = () => {

// Handler for when banner fade-out animation completes
const handleBannerFadeOutComplete = useCallback(() => {
setHideBannerAfterSuccess(true);
// Reset success state for potential future displays
setIsStopLossSuccess(false);
}, []);
// Notify hook that dismiss animation is complete
handleBannerDismissComplete();
}, [handleBannerDismissComplete]);

// Handler for order selection - navigates to order details
const handleOrderSelect = useCallback(
Expand Down Expand Up @@ -985,7 +987,8 @@ const PerpsMarketDetailsView: React.FC<PerpsMarketDetailsViewProps> = () => {
)}

{/* Stop Loss Prompt Banner - Shows when position needs attention */}
{shouldShowBanner && bannerVariant && !hideBannerAfterSuccess && (
{/* Uses hook's isVisible which includes fade-out animation state */}
{isBannerVisible && bannerVariant && (
<PerpsStopLossPromptBanner
variant={bannerVariant}
liquidationDistance={liquidationDistance ?? 0}
Expand All @@ -994,7 +997,7 @@ const PerpsMarketDetailsView: React.FC<PerpsMarketDetailsViewProps> = () => {
onSetStopLoss={handleSetStopLossFromBanner}
onAddMargin={handleAddMarginFromBanner}
isLoading={isSettingStopLoss}
isSuccess={isStopLossSuccess}
isSuccess={isStopLossSuccess || isBannerDismissing}
onFadeOutComplete={handleBannerFadeOutComplete}
testID={
PerpsMarketDetailsViewSelectorsIDs.STOP_LOSS_PROMPT_BANNER
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
formatPerpsFiat,
formatPnl,
formatPercentage,
PRICE_RANGES_MINIMAL_VIEW,
} from '../../utils/formatUtils';
import type {
PerpsNavigationParamList,
Expand Down Expand Up @@ -353,7 +354,11 @@ const PerpsMarketBalanceActions: React.FC<PerpsMarketBalanceActionsProps> = ({
PerpsMarketBalanceActionsSelectorsIDs.AVAILABLE_BALANCE_TEXT
}
>
{formatPerpsFiat(availableBalance)} {strings('perps.available')}
{formatPerpsFiat(availableBalance, {
ranges: PRICE_RANGES_MINIMAL_VIEW,
stripTrailingZeros: false,
})}{' '}
{strings('perps.available')}
</Text>
{hasPositions && !BigNumber(unrealizedPnl).isZero() && (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const styleSheet = (params: { theme: Theme }) => {
// Button styles
button: {
minWidth: 60,
alignSelf: 'center',
},
// Toggle wrapper
toggleContainer: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ export const PerpsTabControlBar: React.FC<PerpsTabControlBarProps> = ({
>
{formatPerpsFiat(totalBalance, {
ranges: PRICE_RANGES_MINIMAL_VIEW,
stripTrailingZeros: false,
})}
</Text>
<Icon
Expand Down
8 changes: 8 additions & 0 deletions app/components/UI/Perps/constants/perpsConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -502,11 +502,19 @@ export const STOP_LOSS_PROMPT_CONFIG = {
// Shows "Set stop loss" banner when ROE drops below this value
ROE_THRESHOLD: -10,

// Minimum loss threshold to show ANY banner (percentage)
// No banner shown until ROE drops below this value
MIN_LOSS_THRESHOLD: -10,

// Debounce duration for ROE threshold (milliseconds)
// User must have ROE below threshold for this duration before showing banner
// Prevents banner from appearing during temporary price fluctuations
ROE_DEBOUNCE_MS: 60_000, // 60 seconds

// Minimum position age before showing any banner (milliseconds)
// Prevents banner from appearing immediately after opening a position
POSITION_MIN_AGE_MS: 60_000, // 60 seconds

// Suggested stop loss ROE percentage
// When suggesting a stop loss, calculate price at this ROE from entry
SUGGESTED_STOP_LOSS_ROE: -50,
Expand Down
Loading
Loading