Skip to content

Commit

Permalink
Merge branch 'develop' of github.com:rainbow-me/rainbow into develop
Browse files Browse the repository at this point in the history
* 'develop' of github.com:rainbow-me/rainbow:
  allow open in new tab (#5610)
  added warning for unknown price impact (#5597)
  fix cloudflare protection (#5609)
  improve type checking on web preferences (#5607)
  fix scrolltoindex firing on last card dismissal (#5606)
  • Loading branch information
BrodyHughes committed Apr 10, 2024
2 parents 2a839e5 + 0cc76b5 commit c5cc83f
Show file tree
Hide file tree
Showing 13 changed files with 217 additions and 94 deletions.
52 changes: 28 additions & 24 deletions src/components/DappBrowser/BrowserContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ interface BrowserContextType {
goBack: () => void;
goForward: () => void;
loadProgress: SharedValue<number> | undefined;
newTabWorklet: () => void;
newTabWorklet: (url?: string) => void;
onRefresh: () => void;
searchInputRef: React.RefObject<TextInput | null>;
searchViewProgress: SharedValue<number> | undefined;
Expand Down Expand Up @@ -74,6 +74,7 @@ interface TabOperation {
type: TabOperationType;
tabId: string;
newActiveIndex: number | undefined;
url?: string;
}

export const RAINBOW_HOME = 'RAINBOW_HOME';
Expand Down Expand Up @@ -107,7 +108,7 @@ const DEFAULT_BROWSER_CONTEXT: BrowserContextType = {
goForward: () => {
return;
},
newTabWorklet: () => {
newTabWorklet: (url?: string) => {
return;
},
onRefresh: () => {
Expand Down Expand Up @@ -311,7 +312,7 @@ export const BrowserContextProvider = ({ children }: { children: React.ReactNode
canGoBack: false,
canGoForward: false,
uniqueId: operation.tabId,
url: RAINBOW_HOME,
url: operation.url || RAINBOW_HOME,
};
newTabStates.push(newTab);
shouldToggleTabView = true;
Expand Down Expand Up @@ -357,27 +358,30 @@ export const BrowserContextProvider = ({ children }: { children: React.ReactNode
tabViewVisible,
]);

const newTabWorklet = useCallback(() => {
'worklet';
const tabIdsInStates = new Set(tabStates?.map(state => state.uniqueId));
const isNewTabOperationPending =
tabOperationQueue.value.some(operation => operation.type === 'newTab') ||
currentlyOpenTabIds.value.some(tabId => !tabIdsInStates.has(tabId));

// The first check is mainly to guard against an edge case that happens when the new tab button is
// pressed just after the last tab is closed, but before a new blank tab has opened programatically,
// which results in two tabs being created when the user most certainly only wanted one.
if (!isNewTabOperationPending && (tabViewVisible.value || currentlyOpenTabIds.value.length === 0)) {
const tabIdForNewTab = generateUniqueIdWorklet();
const newActiveIndex = currentlyOpenTabIds.value.length - 1;

currentlyOpenTabIds.modify(value => {
value.push(tabIdForNewTab);
return value;
});
requestTabOperationsWorklet({ type: 'newTab', tabId: tabIdForNewTab, newActiveIndex });
}
}, [currentlyOpenTabIds, requestTabOperationsWorklet, tabOperationQueue, tabStates, tabViewVisible]);
const newTabWorklet = useCallback(
(url?: string) => {
'worklet';
const tabIdsInStates = new Set(tabStates?.map(state => state.uniqueId));
const isNewTabOperationPending =
tabOperationQueue.value.some(operation => operation.type === 'newTab') ||
currentlyOpenTabIds.value.some(tabId => !tabIdsInStates.has(tabId));

// The first check is mainly to guard against an edge case that happens when the new tab button is
// pressed just after the last tab is closed, but before a new blank tab has opened programatically,
// which results in two tabs being created when the user most certainly only wanted one.
if (url || (!isNewTabOperationPending && (tabViewVisible.value || currentlyOpenTabIds.value.length === 0))) {
const tabIdForNewTab = generateUniqueIdWorklet();
const newActiveIndex = currentlyOpenTabIds.value.length - 1;

currentlyOpenTabIds.modify(value => {
value.push(tabIdForNewTab);
return value;
});
requestTabOperationsWorklet({ type: 'newTab', tabId: tabIdForNewTab, newActiveIndex, url });
}
},
[currentlyOpenTabIds, requestTabOperationsWorklet, tabOperationQueue, tabStates, tabViewVisible]
);

const closeTabWorklet = useCallback(
(tabId: string, tabIndex: number) => {
Expand Down
1 change: 1 addition & 0 deletions src/components/DappBrowser/BrowserTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,7 @@ export const BrowserTab = React.memo(function BrowserTab({ tabId, tabIndex, inje
onLoadProgress={handleOnLoadProgress}
onMessage={handleOnMessage}
onNavigationStateChange={handleNavigationStateChange}
originWhitelist={['*']}
ref={webViewRef}
source={{ uri: tabUrl || RAINBOW_HOME }}
style={styles.webViewStyle}
Expand Down
28 changes: 25 additions & 3 deletions src/components/DappBrowser/DappBrowser.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useEffect, useState } from 'react';
import { StyleSheet } from 'react-native';
import { ScrollView } from 'react-native-gesture-handler';
import Animated, { interpolateColor, useAnimatedProps, useAnimatedStyle } from 'react-native-reanimated';
import Animated, { interpolateColor, runOnJS, useAnimatedProps, useAnimatedReaction, useAnimatedStyle } from 'react-native-reanimated';
import RNFS from 'react-native-fs';

import { Page } from '@/components/layout';
Expand All @@ -15,6 +15,7 @@ import { Search } from './search/Search';
import { TabViewToolbar } from './TabViewToolbar';
import { SheetGestureBlocker } from '../sheet/SheetGestureBlocker';
import { ProgressBar } from './ProgressBar';
import { RouteProp, useRoute } from '@react-navigation/native';

const AnimatedScrollView = Animated.createAnimatedComponent(ScrollView);

Expand All @@ -28,10 +29,33 @@ const getInjectedJS = async () => {
}
};

export type DappBrowserParams = {
url: string;
};

type RouteParams = {
DappBrowserParams: DappBrowserParams;
};

const DappBrowserComponent = () => {
const { isDarkMode } = useColorMode();
const [injectedJS, setInjectedJS] = useState<string | ''>('');

const { scrollViewRef, tabStates, tabViewProgress, tabViewVisible, newTabWorklet, toggleTabViewWorklet } = useBrowserContext();

const route = useRoute<RouteProp<RouteParams, 'DappBrowserParams'>>();

useAnimatedReaction(
() => route.params?.url,
(current, previous) => {
if (current !== previous && route.params?.url) {
newTabWorklet(current);
toggleTabViewWorklet();
}
},
[newTabWorklet, route.params?.url]
);

useEffect(() => {
const loadInjectedJS = async () => {
try {
Expand All @@ -44,8 +68,6 @@ const DappBrowserComponent = () => {
loadInjectedJS();
}, []);

const { scrollViewRef, tabStates, tabViewProgress, tabViewVisible } = useBrowserContext();

useEffect(() => {
pruneScreenshots(tabStates);
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down
6 changes: 6 additions & 0 deletions src/components/cards/remote-cards/RemoteCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,17 @@ export const RemoteCard: React.FC<RemoteCardProps> = ({ card = {} as TrimmedCard
analyticsV2.track(analyticsV2.event.remoteCardDismissed, {
cardKey: cardKey ?? 'unknown-backend-driven-card',
});

const isLastCard = cards.length === 1;

dismissCard(card.sys.id);
if (carouselRef?.current) {
const currentCardIdx = cards.findIndex(c => c.cardKey === cardKey);
if (currentCardIdx === -1) return;

// check if this is the last card and don't scroll if so
if (isLastCard) return;

carouselRef.current.scrollToIndex({
index: currentCardIdx,
animated: true,
Expand Down
3 changes: 3 additions & 0 deletions src/components/exchange/ExchangeDetailsRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ interface ExchangeDetailsRowProps {
priceImpactColor?: string;
priceImpactNativeAmount?: string | null;
priceImpactPercentDisplay?: string | null;
outputCurrencySymbol?: string | null;
type: string;
}

Expand All @@ -32,6 +33,7 @@ export default function ExchangeDetailsRow({
priceImpactColor,
priceImpactNativeAmount,
priceImpactPercentDisplay,
outputCurrencySymbol,
type,
}: ExchangeDetailsRowProps) {
const detailsRowOpacity = useSharedValue(1);
Expand Down Expand Up @@ -83,6 +85,7 @@ export default function ExchangeDetailsRow({
priceImpactColor={priceImpactColor}
priceImpactNativeAmount={priceImpactNativeAmount}
priceImpactPercentDisplay={priceImpactPercentDisplay}
outputCurrencySymbol={outputCurrencySymbol}
style={priceImpactAnimatedStyle}
/>
<Box
Expand Down
31 changes: 21 additions & 10 deletions src/components/exchange/PriceImpactWarning.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import Animated from 'react-native-reanimated';
import { ButtonPressAnimation } from '../animations';
import { Box, ColorModeProvider, Inline, Text } from '@/design-system';
import { position } from '@/styles';
import { NO_PRICE_DATA_PERCENTAGE } from '@/hooks/usePriceImpactDetails';

interface PriceImpactWarningProps extends ViewProps {
onPress: () => void;
isHighPriceImpact: boolean;
priceImpactColor?: string;
priceImpactNativeAmount?: string | null;
priceImpactPercentDisplay?: string | null;
outputCurrencySymbol?: string | null;
style?: StyleProp<ViewStyle>;
}

Expand All @@ -21,29 +23,38 @@ export default function PriceImpactWarning({
priceImpactColor = 'primary',
priceImpactNativeAmount,
priceImpactPercentDisplay,
outputCurrencySymbol,
style,
...props
}: PriceImpactWarningProps) {
const headingValue = priceImpactNativeAmount ?? priceImpactPercentDisplay;
const hasPriceData = priceImpactPercentDisplay !== NO_PRICE_DATA_PERCENTAGE;
const impactMsg = !hasPriceData
? `${outputCurrencySymbol} ${lang.t('exchange.price_impact.no_data')}`
: lang.t('exchange.price_impact.small_market');
return (
<ColorModeProvider value="dark">
<Animated.View {...props} style={[style, position.coverAsObject]}>
{isHighPriceImpact && headingValue && (
{!isHighPriceImpact && headingValue && (
<ButtonPressAnimation onPress={onPress} scaleTo={0.94}>
<Box paddingHorizontal="19px (Deprecated)" paddingTop="19px (Deprecated)">
<Inline alignHorizontal="center">
<Text weight="bold" size="17pt" color={{ custom: priceImpactColor }}>{`􀇿 `}</Text>
<Text weight="bold" size="17pt" color="primary (Deprecated)">
{lang.t('exchange.price_impact.small_market')}
</Text>
<Text
weight="bold"
size="17pt"
color={{ custom: priceImpactColor }}
>{` • ${lang.t('exchange.price_impact.losing_prefix')} `}</Text>
<Text weight="bold" size="17pt" color={{ custom: priceImpactColor }}>
{headingValue}
{impactMsg}
</Text>
{hasPriceData && (
<Text
weight="bold"
size="17pt"
color={{ custom: priceImpactColor }}
>{` • ${lang.t('exchange.price_impact.losing_prefix')} `}</Text>
)}
{hasPriceData && (
<Text weight="bold" size="17pt" color={{ custom: priceImpactColor }}>
{headingValue}
</Text>
)}
</Inline>
</Box>
</ButtonPressAnimation>
Expand Down
1 change: 1 addition & 0 deletions src/components/expanded-state/SwapDetailsState.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export default function SwapDetailsState({ confirmButtonProps, restoreFocusOnSwa
/>
<SwapDetailsSlippageMessage
isHighPriceImpact={priceImpact.type !== SwapPriceImpactType.none}
outputCurrencySymbol={outputCurrency?.symbol}
onLayout={setSlippageMessageHeight}
priceImpactColor={priceImpact.color}
priceImpactNativeAmount={priceImpact.impactDisplay}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Centered, Column, ColumnWithMargins, Row } from '../../layout';
import { Emoji, Text } from '../../text';
import styled from '@/styled-thing';
import { padding } from '@/styles';
import { NO_PRICE_DATA_PERCENTAGE } from '@/hooks/usePriceImpactDetails';

const Container = styled(ColumnWithMargins).attrs({
align: 'center',
Expand All @@ -29,24 +30,39 @@ export default function SwapDetailsSlippageMessage({
priceImpactColor,
priceImpactNativeAmount,
priceImpactPercentDisplay,
outputCurrencySymbol,
...props
}) {
const { colors } = useTheme();
const headingValue = priceImpactNativeAmount ?? priceImpactPercentDisplay;
const hasPriceData = priceImpactPercentDisplay !== NO_PRICE_DATA_PERCENTAGE;
const impactMsg = `${outputCurrencySymbol} ${lang.t('exchange.price_impact.no_data')}`;
return isHighPriceImpact ? (
<Column align="center" {...props}>
<Container>
<Row align="center">
<Heading color={priceImpactColor} weight="heavy">
{lang.t('expanded_state.swap.losing')}{' '}
</Heading>
<Heading color={priceImpactColor} letterSpacing="roundedTight" weight="heavy">
{headingValue}
</Heading>
<Emoji size="larger"> 🥵</Emoji>
</Row>
<Message>{lang.t('expanded_state.swap.slippage_message')}</Message>
</Container>
{hasPriceData ? (
<Container>
<Row align="center">
<Heading color={priceImpactColor} weight="heavy">
{lang.t('expanded_state.swap.losing')}{' '}
</Heading>
<Heading color={priceImpactColor} letterSpacing="roundedTight" weight="heavy">
{headingValue}
</Heading>
<Emoji size="larger"> 🥵</Emoji>
</Row>
<Message>{lang.t('expanded_state.swap.slippage_message')}</Message>
</Container>
) : (
<Container>
<Row align="center">
<Text weight="bold" size="17pt" color={{ custom: priceImpactColor }}>{`􀇿 `}</Text>
<Text weight="bold" size="17pt" color="primary (Deprecated)">
{impactMsg}
</Text>
</Row>
<Message>{lang.t('exchange.price_impact.no_data_subtitle')}</Message>
</Container>
)}
<Centered width={139}>
<Divider color={colors.rowDividerExtraLight} inset={false} />
</Centered>
Expand Down
1 change: 1 addition & 0 deletions src/hooks/usePriceImpactDetails.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export enum SwapPriceImpactType {

const PriceImpactWarningThreshold = 0.05;
const SeverePriceImpactThreshold = 0.1;
export const NO_PRICE_DATA_PERCENTAGE = '100.00%';

export default function usePriceImpactDetails(
inputCurrency: SwappableAsset | null,
Expand Down
Loading

0 comments on commit c5cc83f

Please sign in to comment.