Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: expanded state v2 phase 1 #6332

Draft
wants to merge 23 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
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
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { borders, colors, padding, shadow } from '@/styles';
import RainbowCoinIcon from '@/components/coin-icon/RainbowCoinIcon';
import { NativeCurrencyKey } from '@/entities';
import { ChainId } from '@/state/backendNetworks/types';
import { EXPANDED_STATE_V2, useExperimentalFlag } from '@/config';
import { useRemoteConfig } from '@/model/remoteConfig';

interface CoinCheckButtonProps {
isHidden: boolean;
Expand Down Expand Up @@ -67,19 +69,25 @@ interface MemoizedBalanceCoinRowProps {
const MemoizedBalanceCoinRow = React.memo(
({ uniqueId, nativeCurrency, theme, navigate, nativeCurrencySymbol, isHidden, maybeCallback }: MemoizedBalanceCoinRowProps) => {
const item = useAccountAsset(uniqueId, nativeCurrency);
const { expanded_state_v2 } = useRemoteConfig();
const expandedStateV2Enabled = useExperimentalFlag(EXPANDED_STATE_V2) || expanded_state_v2;

const handlePress = useCallback(() => {
if (maybeCallback.current) {
maybeCallback.current();
} else {
navigate(Routes.EXPANDED_ASSET_SHEET, {
asset: item,
fromDiscover: true,
isFromWalletScreen: true,
type: 'token',
});
if (expandedStateV2Enabled) {
navigate(Routes.EXPANDED_ASSET_SHEET_V2, { asset: item });
} else {
navigate(Routes.EXPANDED_ASSET_SHEET, {
asset: item,
fromDiscover: true,
isFromWalletScreen: true,
type: 'token',
});
}
}
}, [navigate, item, maybeCallback]);
}, [navigate, item, maybeCallback, expandedStateV2Enabled]);

const percentChange = item?.native?.change || undefined;
const percentageChangeDisplay = formatPercentageString(percentChange);
Expand Down
2 changes: 2 additions & 0 deletions src/config/experimental.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const DEGEN_MODE = 'Degen Mode';
export const FEATURED_RESULTS = 'Featured Results';
export const CLAIMABLES = 'Claimables';
export const NFTS_ENABLED = 'Nfts Enabled';
export const EXPANDED_STATE_V2 = 'Expanded State V2';
export const TRENDING_TOKENS = 'Trending Tokens';

/**
Expand Down Expand Up @@ -67,6 +68,7 @@ export const defaultConfig: Record<string, ExperimentalValue> = {
[FEATURED_RESULTS]: { settings: true, value: false },
[CLAIMABLES]: { settings: true, value: false },
[NFTS_ENABLED]: { settings: true, value: !!IS_TEST },
[EXPANDED_STATE_V2]: { settings: true, value: false },
[TRENDING_TOKENS]: { settings: true, value: false },
};

Expand Down
6 changes: 5 additions & 1 deletion src/model/remoteConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export interface RainbowConfig extends Record<string, string | boolean | number>
featured_results: boolean;
claimables: boolean;
nfts_enabled: boolean;
expanded_state_v2: boolean;

trending_tokens_limit: number;
}
Expand Down Expand Up @@ -149,6 +150,7 @@ export const DEFAULT_CONFIG: RainbowConfig = {
featured_results: true,
claimables: true,
nfts_enabled: true,
expanded_state_v2: false,

trending_tokens_limit: 10,
trending_tokens_enabled: false,
Expand Down Expand Up @@ -208,7 +210,9 @@ export async function fetchRemoteConfig(): Promise<RainbowConfig> {
key === 'featured_results' ||
key === 'claimables' ||
key === 'nfts_enabled' ||
key === 'trending_tokens_enabled'
key === 'expanded_state_v2' ||
key === 'trending_tokens_enabled' ||
key === 'expanded_state_v2'
) {
config[key] = entry.asBoolean();
} else if (key === 'trending_tokens_limit') {
Expand Down
2 changes: 2 additions & 0 deletions src/navigation/Routes.android.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import ConnectedDappsSheet from '../screens/ConnectedDappsSheet';
import ENSAdditionalRecordsSheet from '../screens/ENSAdditionalRecordsSheet';
import ENSConfirmRegisterSheet from '../screens/ENSConfirmRegisterSheet';
import ExpandedAssetSheet from '../screens/ExpandedAssetSheet';
import { ExpandedAssetSheet as ExpandedAssetSheetV2 } from '@/screens/expandedAssetSheet/ExpandedAssetSheet';
import ExplainSheet from '../screens/ExplainSheet';
import ExternalLinkWarningSheet from '../screens/ExternalLinkWarningSheet';
import ModalScreen from '../screens/ModalScreen';
Expand Down Expand Up @@ -250,6 +251,7 @@ function BSNavigator() {
<BSStack.Screen component={ClaimClaimablePanel} name={Routes.CLAIM_CLAIMABLE_PANEL} />
<BSStack.Screen component={ChangeWalletSheet} name={Routes.CHANGE_WALLET_SHEET} options={{ ...bottomSheetPreset }} />
<BSStack.Screen component={SwapScreen} name={Routes.SWAP} options={swapSheetPreset} />
<BSStack.Screen component={ExpandedAssetSheetV2} name={Routes.EXPANDED_ASSET_SHEET_V2} />
</BSStack.Navigator>
);
}
Expand Down
3 changes: 3 additions & 0 deletions src/navigation/Routes.ios.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import ConnectedDappsSheet from '../screens/ConnectedDappsSheet';
import ENSAdditionalRecordsSheet from '../screens/ENSAdditionalRecordsSheet';
import ENSConfirmRegisterSheet from '../screens/ENSConfirmRegisterSheet';
import ExpandedAssetSheet from '../screens/ExpandedAssetSheet';
import { ExpandedAssetSheet as ExpandedAssetSheetV2 } from '@/screens/expandedAssetSheet/ExpandedAssetSheet';
import ExplainSheet from '../screens/ExplainSheet';
import ExternalLinkWarningSheet from '../screens/ExternalLinkWarningSheet';
import ModalScreen from '../screens/ModalScreen';
Expand Down Expand Up @@ -70,6 +71,7 @@ import {
swapConfig,
checkIdentifierSheetConfig,
recieveModalSheetConfig,
expandedAssetSheetV2Config,
networkSelectorConfig,
} from './config';
import { addCashSheet, emojiPreset, emojiPresetWallet, overlayExpandedPreset, sheetPreset } from './effects';
Expand Down Expand Up @@ -282,6 +284,7 @@ function NativeStackNavigator() {
<NativeStack.Screen component={ClaimRewardsPanel} name={Routes.CLAIM_REWARDS_PANEL} {...panelConfig} />
<NativeStack.Screen component={ClaimClaimablePanel} name={Routes.CLAIM_CLAIMABLE_PANEL} {...panelConfig} />
<NativeStack.Screen component={SwapScreen} name={Routes.SWAP} {...swapConfig} />
<NativeStack.Screen component={ExpandedAssetSheetV2} name={Routes.EXPANDED_ASSET_SHEET_V2} {...expandedAssetSheetV2Config} />
</NativeStack.Navigator>
);
}
Expand Down
13 changes: 13 additions & 0 deletions src/navigation/config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,19 @@ export const panelConfig = {
}),
};

export const expandedAssetSheetV2Config = {
options: ({ route: { params = {} } }) => ({
...buildCoolModalConfig({
...params,
backgroundOpacity: 1,
cornerRadius: 'device',
springDamping: 1,
topOffset: 0,
transitionDuration: 0.3,
}),
}),
};

export const swapConfig = {
options: ({ route: { params = {} } }) => ({
...buildCoolModalConfig({
Expand Down
1 change: 1 addition & 0 deletions src/navigation/routesNames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const Routes = {
ENS_SEARCH_SHEET: 'ENSSearchSheet',
EXPANDED_ASSET_SCREEN: 'ExpandedAssetScreen',
EXPANDED_ASSET_SHEET: 'ExpandedAssetSheet',
EXPANDED_ASSET_SHEET_V2: 'ExpandedAssetSheetV2',
EXPLAIN_SHEET: 'ExplainSheet',
PORTAL: 'Portal',
EXTERNAL_LINK_WARNING_SHEET: 'ExternalLinkWarningSheet',
Expand Down
55 changes: 55 additions & 0 deletions src/screens/expandedAssetSheet/ExpandedAssetSheet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React, { useEffect } from 'react';
import { ExpandedAssetSheetContextProvider } from './context/ExpandedAssetSheetContext';
import { ParsedAddressAsset } from '@/entities';
import { RouteProp, useRoute } from '@react-navigation/native';
import { SheetContent } from './components/SheetContent';
import { SimpleSheet } from '@/components/sheet/SimpleSheet';
import { colors } from '@/styles';
import { SlackSheet } from '@/components/sheet';
import { IS_ANDROID, IS_IOS } from '@/env';
import { StatusBar } from 'react-native';
import { useSharedValue } from 'react-native-reanimated';
import { StatusBarHelper } from '@/helpers';
import { Box, Cover } from '@/design-system';

export type ExpandedAssetSheetParams = {
asset: ParsedAddressAsset;
};

type RouteParams = {
ExpandedAssetSheetParams: ExpandedAssetSheetParams;
};

export function ExpandedAssetSheet() {
const {
params: { asset },
} = useRoute<RouteProp<RouteParams, 'ExpandedAssetSheetParams'>>();

const yPosition = useSharedValue(0);

useEffect(() => StatusBarHelper.setLightContent(), []);

return (
<ExpandedAssetSheetContextProvider asset={asset}>
<SlackSheet
backgroundColor={asset.colors?.primary ?? colors.appleBlue}
{...(IS_IOS ? { height: '100%' } : {})}
scrollEnabled
removeTopPadding
hideHandle
additionalTopPadding={IS_ANDROID ? StatusBar.currentHeight : false}
yPosition={yPosition}
>
<Box position="absolute" width="full" style={{ height: '200%', backgroundColor: 'rgba(0, 0, 0, 0.8)' }} />
<Box
height={{ custom: 5 }}
width={{ custom: 36 }}
borderRadius={3}
position="absolute"
style={{ backgroundColor: 'rgba(245, 248, 255, 0.3)', top: 63, alignSelf: 'center' }}
/>
<SheetContent />
</SlackSheet>
</ExpandedAssetSheetContextProvider>
);
}
35 changes: 35 additions & 0 deletions src/screens/expandedAssetSheet/components/SheetContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from 'react';
import { SectionId, useExpandedAssetSheetContext } from '../context/ExpandedAssetSheetContext';
import { AccentColorProvider, Box, ColorModeProvider, Separator, Stack } from '@/design-system';
import { CollapsibleSection, LAYOUT_ANIMATION } from './shared/CollapsibleSection';
import Animated from 'react-native-reanimated';
import { AboutSection, BalanceSection, BridgeSection, BuySection } from './sections';
import { ScrollView } from 'react-native';
import { deviceUtils } from '@/utils';

export function SheetContent() {
const { accentColors, asset } = useExpandedAssetSheetContext();
return (
<AccentColorProvider color={accentColors.opacity100}>
<ScrollView style={{ height: deviceUtils.dimensions.height }} contentContainerStyle={{ minHeight: deviceUtils.dimensions.height }}>
<ColorModeProvider value="dark">
<Box height="full" width="full" paddingTop={{ custom: 96 }} paddingBottom={{ custom: 47 }} paddingHorizontal="24px">
<Stack
space="28px"
separator={
<Animated.View layout={LAYOUT_ANIMATION}>
<Separator color={{ custom: 'rgba(245, 248, 255, 0.03)' }} thickness={1} />
</Animated.View>
}
>
<BalanceSection />
<CollapsibleSection content={<BuySection />} icon="􀋥" id={SectionId.BUY} primaryText="Buy" secondaryText={asset.symbol} />
<CollapsibleSection content={<BridgeSection />} icon="􀄹" id={SectionId.BRIDGE} primaryText="Bridge" secondaryText="to" />
<CollapsibleSection content={<AboutSection />} icon="􁜾" id={SectionId.ABOUT} primaryText="About" />
</Stack>
</Box>
</ColorModeProvider>
</ScrollView>
</AccentColorProvider>
);
}
140 changes: 140 additions & 0 deletions src/screens/expandedAssetSheet/components/sections/AboutSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import React, { memo, useMemo } from 'react';
import { Bleed, Box, IconContainer, Inline, Stack, Text, TextShadow } from '@/design-system';
import { Row } from '../shared/Row';
import { useExpandedAssetSheetContext } from '../../context/ExpandedAssetSheetContext';
import { ButtonPressAnimation } from '@/components/animations';
import { useAccountSettings, useAdditionalAssetData } from '@/hooks';
import { Linking } from 'react-native';
import { formatURLForDisplay } from '@/utils';
import { XIcon } from '../../icons/XIcon';

interface RowItem {
icon?: string;
iconName?: string;
title: string;
url: string;
value?: string;
}

interface RowButtonProps {
highlighted?: boolean;
icon?: string;
iconName?: string;
title: string;
url: string;
value?: string;
}

function RowButton({ highlighted, icon, iconName, title, url, value }: RowButtonProps) {
const { accentColors } = useExpandedAssetSheetContext();

return (
<ButtonPressAnimation onPress={() => Linking.openURL(url)} scaleTo={0.96}>
<Row highlighted={highlighted}>
<Inline space="12px" alignVertical="center">
{icon && (
<IconContainer height={10} width={20}>
<TextShadow blur={12} shadowOpacity={0.24}>
<Text weight="medium" align="center" size="15pt" color="accent">
{icon}
</Text>
</TextShadow>
</IconContainer>
)}
{iconName && (
<Bleed left="8px" vertical="24px">
<XIcon color={accentColors.opacity100} size={38} />
</Bleed>
)}
<TextShadow blur={12} shadowOpacity={0.24}>
<Text weight="semibold" size="17pt" color="accent">
{title}
</Text>
</TextShadow>
</Inline>
<Inline space="8px" alignVertical="center">
{value && (
<Text weight="semibold" align="right" size="17pt" color={{ custom: accentColors.opacity56 }}>
{value}
</Text>
)}
<IconContainer height={9} width={16}>
<TextShadow blur={12} shadowOpacity={0.24}>
<Text weight="bold" align="center" size="15pt" color="accent">
􀄯
</Text>
</TextShadow>
</IconContainer>
</Inline>
</Row>
</ButtonPressAnimation>
);
}

export const AboutSection = memo(function AboutSection() {
const { asset } = useExpandedAssetSheetContext();
const { nativeCurrency } = useAccountSettings();
const { data: metadata } = useAdditionalAssetData({
address: asset.address,
chainId: asset.chainId,
currency: nativeCurrency,
});

const rowItems = useMemo(() => {
const items: RowItem[] = [];

if (metadata?.links?.homepage?.url) {
items.push({
icon: '􀎞',
title: 'Website',
url: metadata.links.homepage.url,
value: formatURLForDisplay(metadata.links.homepage.url),
});
}

if (metadata?.links?.twitter?.url) {
items.push({
iconName: 'x',
title: 'Twitter',
url: metadata.links.twitter.url,
value: `@${metadata.links.twitter.url.split('/').pop()}`,
});
}

items.push({
icon: '􀊫',
title: 'Search on Twitter',
url: `https://x.com/search?q=${asset.name}`,
});

return items;
}, [asset.name, metadata?.links?.homepage, metadata?.links?.twitter]);

return (
<Box gap={40}>
<Stack space="4px">
{rowItems.map((item, index) => (
<RowButton
key={`${item.title}-${index}`}
highlighted={index % 2 === 0}
icon={item.icon}
iconName={item.iconName}
title={item.title}
url={item.url}
value={item.value}
/>
))}
</Stack>
{metadata?.description && (
<Box gap={24}>
<Text weight="bold" size="20pt" color="labelSecondary">
What is {asset.name}?
</Text>
<Text weight="medium" size="17pt / 150%" color="labelTertiary">
{metadata.description}
</Text>
</Box>
)}
</Box>
);
});
Loading
Loading