Skip to content

Commit

Permalink
feat: support drag-and-drop and long-press in the market list. (#6579)
Browse files Browse the repository at this point in the history
  • Loading branch information
huhuanming authored Jan 22, 2025
1 parent ae86021 commit bea6674
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 82 deletions.
45 changes: 40 additions & 5 deletions packages/components/src/composite/Table/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { PropsWithChildren, ReactElement } from 'react';
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';

import { StyleSheet } from 'react-native';
import { globalRef } from 'react-native-draggable-flatlist/src/context/globalRef';
import { getTokenValue, useMedia, withStaticProperties } from 'tamagui';

import platformEnv from '@onekeyhq/shared/src/platformEnv';
Expand Down Expand Up @@ -143,20 +144,53 @@ function TableRow<T>({
showSkeleton?: boolean;
isActive?: boolean;
}) {
const { md } = useMedia();
const onRowEvents = useMemo(() => onRow?.(item, index), [index, item, onRow]);
const handlePress = useCallback(() => {
onRowEvents?.onPress?.();
}, [onRowEvents]);
const itemPressStyle = pressStyle ? listItemPressStyle : undefined;
const isDragging = pressStyle && isActive;
const pressTimeRef = useRef(0);

const handlePressIn = useCallback(() => {
pressTimeRef.current = Date.now();
}, []);

const getTimeDiff = useCallback(() => Date.now() - pressTimeRef.current, []);

const handlePress = useCallback(() => {
if (platformEnv.isNative) {
onRowEvents?.onPress?.();
} else if (getTimeDiff() < 350) {
onRowEvents?.onPress?.();
}
}, [getTimeDiff, onRowEvents]);

const handleLongPress = useCallback(() => {
if (platformEnv.isNative) {
if (draggable) {
drag?.();
setTimeout(() => {
if (globalRef.translationY === 0) {
Haptics.impact(ImpactFeedbackStyle.Medium);
globalRef.reset();
onRowEvents?.onLongPress?.();
}
}, 650);
} else {
onRowEvents?.onLongPress?.();
}
} else if (getTimeDiff() >= 350) {
onRowEvents?.onLongPress?.();
}
}, [drag, draggable, getTimeDiff, onRowEvents]);
return (
<XStack
minHeight={DEFAULT_ROW_HEIGHT}
onPress={handlePress}
bg={isDragging ? '$bgActive' : '$bgApp'}
borderRadius="$3"
dataSet={!platformEnv.isNative && draggable ? dataSet : undefined}
onLongPress={platformEnv.isNative && draggable ? drag : undefined}
onPressIn={!platformEnv.isNative ? handlePressIn : undefined}
onPress={handlePress}
onLongPress={md ? handleLongPress : undefined}
{...itemPressStyle}
{...rowProps}
>
Expand Down Expand Up @@ -253,6 +287,7 @@ export interface ITableProps<T> {
) =>
| {
onPress?: () => void;
onLongPress?: () => void;
}
| undefined;
}
Expand Down
6 changes: 3 additions & 3 deletions packages/kit/src/views/Earn/EarnHome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -831,7 +831,7 @@ function BasicEarnHome() {
isLoading={false}
leftIconButtonStyle={{
...bannerIconStyle,
left: '$3.5',
left: media.md ? 34 : '$3.5',
}}
rightIconButtonStyle={{
...bannerIconStyle,
Expand All @@ -848,7 +848,7 @@ function BasicEarnHome() {
top: 0,
bottom: 0,
right: '$5',
left: '$5',
left: media.md ? '$10' : '$5',
justifyContent: 'center',
}}
/>
Expand All @@ -863,7 +863,7 @@ function BasicEarnHome() {
width="100%"
/>
);
}, [earnBanners, media.gtLg, onBannerPress]);
}, [earnBanners, media.gtLg, media.md, onBannerPress]);

return (
<Page fullPage>
Expand Down
137 changes: 64 additions & 73 deletions packages/kit/src/views/Market/components/MarketHomeList.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { MutableRefObject } from 'react';
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';

import { useIntl } from 'react-intl';
Expand All @@ -16,9 +17,7 @@ import type {
} from '@onekeyhq/components';
import {
ActionList,
Badge,
Icon,
IconButton,
NumberSizeableText,
Select,
SizableText,
Expand Down Expand Up @@ -153,13 +152,15 @@ function MarketMdColumn({
currency,
mdColumnKeys,
showMoreAction,
onLongPressRefs,
tabIndex,
isWatchList,
}: {
item: IMarketToken;
currency: string;
isWatchList: boolean;
mdColumnKeys: (keyof IMarketToken)[];
onLongPressRefs: MutableRefObject<Record<string, () => void>>;
showMoreAction: boolean;
tabIndex?: number;
}) {
Expand All @@ -175,11 +176,6 @@ function MarketMdColumn({
[item.symbol],
);

const { checked, setIsChecked } = useStarChecked({
tabIndex,
coingeckoId: item.coingeckoId,
from: EWatchlistFrom.catalog,
});
const handleMdItemAction = useCallback(async () => {
const { coingeckoId, symbol } = item;
const isInWatchList = actions.isInWatchList(coingeckoId);
Expand All @@ -203,9 +199,6 @@ function MarketMdColumn({
}),
onPress: () => {
actions.removeFormWatchList(coingeckoId);
if (!isWatchList) {
setIsChecked(false);
}
defaultLogger.market.token.removeFromWatchlist({
tokenSymbol: coingeckoId,
removeWatchlistFrom: EWatchlistFrom.catalog,
Expand All @@ -219,9 +212,6 @@ function MarketMdColumn({
}),
onPress: () => {
actions.addIntoWatchList(coingeckoId);
if (!isWatchList) {
setIsChecked(true);
}
defaultLogger.market.token.addToWatchList({
tokenSymbol: coingeckoId,
addWatchlistFrom: EWatchlistFrom.catalog,
Expand Down Expand Up @@ -297,14 +287,18 @@ function MarketMdColumn({
actions,
canStaking,
intl,
isWatchList,
item,
setIsChecked,
showBuyOrSellButton,
showMoreAction,
tradeActions,
]);
const isPositive = Number(item.priceChangePercentage24H) >= 0;

useEffect(() => {
if (onLongPressRefs.current) {
onLongPressRefs.current[item.coingeckoId] = handleMdItemAction;
}
}, [item.coingeckoId, handleMdItemAction, onLongPressRefs]);

return (
<XStack
height={60}
Expand All @@ -315,19 +309,16 @@ function MarketMdColumn({
px="$5"
>
<XStack gap="$3" ai="center">
<MarketTokenStarIcon
url={item.image}
checked={isWatchList ? false : checked}
/>
<MarketTokenIcon uri={item.image} size="$10" />
<YStack>
<SizableText size="$bodyLgMedium" userSelect="none">
{item.symbol.toUpperCase()}
</SizableText>
<SizableText size="$bodyMd" color="$textSubdued" userSelect="none">
<SizableText size="$bodySm" color="$textSubdued" userSelect="none">
{`VOL `}
<NumberSizeableText
userSelect="none"
size="$bodyMd"
size="$bodySm"
formatter="marketCap"
color="$textSubdued"
formatterOptions={{ currency }}
Expand All @@ -337,61 +328,58 @@ function MarketMdColumn({
</SizableText>
</YStack>
</XStack>
<XStack gap="$3.5" ai="center">
<YStack ai="flex-end" flexShrink={1}>
{mdColumnKeys[0] === 'price' ? (
<MarketTokenPrice
numberOfLines={1}
flexShrink={1}
size="$bodyLgMedium"
price={String(item[mdColumnKeys[0]])}
tokenName={item.name}
tokenSymbol={item.symbol}
lastUpdated={item.lastUpdated}
/>
) : (
<XStack ai="center" gap="$5" flexShrink={1}>
{mdColumnKeys[0] === 'price' ? (
<MarketTokenPrice
numberOfLines={1}
flexShrink={1}
size="$bodyLgMedium"
price={String(item[mdColumnKeys[0]])}
tokenName={item.name}
tokenSymbol={item.symbol}
lastUpdated={item.lastUpdated}
/>
) : (
<NumberSizeableText
userSelect="none"
flexShrink={1}
numberOfLines={1}
size="$bodyLgMedium"
formatter="marketCap"
formatterOptions={{ currency }}
>
{item[mdColumnKeys[0]] as string}
</NumberSizeableText>
)}
{item[mdColumnKeys[1]] ? (
<XStack
width="$20"
height="$8"
jc="center"
ai="center"
backgroundColor={
Number(item.priceChangePercentage24H) > 0
? '$bgSuccessStrong'
: '$bgCriticalStrong'
}
borderRadius="$2"
>
<NumberSizeableText
adjustsFontSizeToFit
numberOfLines={platformEnv.isNative ? 1 : 2}
px="$1"
userSelect="none"
flexShrink={1}
numberOfLines={1}
size="$bodyLgMedium"
formatter="marketCap"
formatterOptions={{ currency }}
size="$bodyMdMedium"
color="white"
formatter="priceChange"
formatterOptions={{ showPlusMinusSigns: true }}
>
{item[mdColumnKeys[0]] as string}
{item[mdColumnKeys[1]] as string}
</NumberSizeableText>
)}
{item[mdColumnKeys[1]] ? (
<Badge
badgeSize="sm"
badgeType={isPositive ? 'success' : 'critical'}
px={0}
width={66}
jc="center"
>
<NumberSizeableText
adjustsFontSizeToFit
numberOfLines={platformEnv.isNative ? 1 : 2}
px="$1"
userSelect="none"
size="$bodySmMedium"
formatter="priceChange"
color={isPositive ? '$textSuccess' : '$textCritical'}
formatterOptions={{ showPlusMinusSigns: true }}
>
{item[mdColumnKeys[1]] as string}
</NumberSizeableText>
</Badge>
) : (
<MdPlaceholder />
)}
</YStack>
<IconButton
icon="DotVerOutline"
size="small"
variant="tertiary"
onPress={handleMdItemAction}
/>
</XStack>
) : (
<MdPlaceholder />
)}
</XStack>
</XStack>
);
Expand Down Expand Up @@ -492,6 +480,7 @@ function BasicMarketHomeList({
);

const { width: screenWidth } = useWindowDimensions();
const onLongPressRefs = useRef<Record<string, () => void>>({});

const [settings] = useSettingsPersistAtom();
const currency = settings.currencyInfo.symbol;
Expand All @@ -500,6 +489,7 @@ function BasicMarketHomeList({
(item: IMarketToken) => (
<MarketMdColumn
item={item}
onLongPressRefs={onLongPressRefs}
isWatchList={!!draggable}
tabIndex={tabIndex}
currency={currency}
Expand Down Expand Up @@ -958,6 +948,7 @@ function BasicMarketHomeList({
const onRow = useCallback(
(record: IMarketToken) => ({
onPress: () => toDetailPage(record),
onLongPress: () => onLongPressRefs.current[record.coingeckoId]?.(),
}),
[toDetailPage],
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const SwapSlippageTriggerContainer = ({
[displaySlippage, intl, slippageItem.key],
);

const debounceOnPress = useDebouncedCallback(onPress, 200);
const debounceOnPress = useDebouncedCallback(onPress, 350);

const valueComponent = useMemo(
() => (
Expand Down
Loading

0 comments on commit bea6674

Please sign in to comment.