From a11dc5209b7653af641dcc01597b24b14dc4630b Mon Sep 17 00:00:00 2001 From: youngkidwarrior Date: Sat, 18 Jan 2025 00:29:21 -0800 Subject: [PATCH] Swipe to reply send --- apps/expo/package.json | 2 +- apps/next/pages/_app.tsx | 9 +- .../app/components/sidebar/HomeSideBar.tsx | 11 +- .../app/features/activity/RecentActivity.tsx | 13 +- .../app/features/home/TokenActivityRow.tsx | 233 ++- packages/app/features/home/TokenDetails.tsx | 2 +- .../app/features/home/TokenDetailsHistory.tsx | 20 +- .../__snapshots__/TokenDetails.test.tsx.snap | 1665 ++++++++++++----- packages/app/package.json | 1 + packages/app/utils/distributions.ts | 2 +- yarn.lock | 13 +- 11 files changed, 1368 insertions(+), 603 deletions(-) diff --git a/apps/expo/package.json b/apps/expo/package.json index 8bba745dc..4efbc9a36 100644 --- a/apps/expo/package.json +++ b/apps/expo/package.json @@ -43,7 +43,7 @@ "react-dom": "^18.2.0", "react-native": "0.74.1", "react-native-dotenv": "^3.4.9", - "react-native-gesture-handler": "~2.14.1", + "react-native-gesture-handler": "^2.22.0", "react-native-ios-modal": "^0.1.8", "react-native-reanimated": "~3.10.1", "react-native-safe-area-context": "4.8.2", diff --git a/apps/next/pages/_app.tsx b/apps/next/pages/_app.tsx index a3f5c7195..d64dc1f3f 100644 --- a/apps/next/pages/_app.tsx +++ b/apps/next/pages/_app.tsx @@ -15,6 +15,7 @@ import { Provider } from 'app/provider' import { YStack, H1, H2 } from '@my/ui' import { IconSendLogo } from 'app/components/icons' import { SendV0TokenUpgradeScreen } from 'app/features/send-token-upgrade/screen' +import { GestureHandlerRootView } from 'react-native-gesture-handler' if (process.env.NODE_ENV === 'production') { require('../public/tamagui.css') @@ -65,9 +66,11 @@ function MyApp({ {/* TODO: create a concerns screen or move to provider instead of wrapping here in next app */} - - {getLayout()} - + + + {getLayout()} + + diff --git a/packages/app/components/sidebar/HomeSideBar.tsx b/packages/app/components/sidebar/HomeSideBar.tsx index 11b93bb09..9f224a037 100644 --- a/packages/app/components/sidebar/HomeSideBar.tsx +++ b/packages/app/components/sidebar/HomeSideBar.tsx @@ -28,6 +28,7 @@ import { NavSheet } from '../NavSheet' import { useUser } from 'app/utils/useUser' import { ReferralLink } from '../ReferralLink' import { useHoverStyles } from 'app/utils/useHoverStyles' +import { useIsSendingUnlocked } from 'app/utils/useIsSendingUnlocked' const links = [ { @@ -60,6 +61,8 @@ const links = [ ].filter(Boolean) as { icon: ReactElement; text: string; href: string }[] const HomeSideBar = ({ ...props }: YStackProps) => { + const isSendingUnlocked = useIsSendingUnlocked() + return ( @@ -67,9 +70,11 @@ const HomeSideBar = ({ ...props }: YStackProps) => { - {links.map((link) => ( - - ))} + {links + .filter((link) => (isSendingUnlocked ? true : link.href !== '/send')) + .map((link) => ( + + ))} ) diff --git a/packages/app/features/activity/RecentActivity.tsx b/packages/app/features/activity/RecentActivity.tsx index bce6cf1c5..894508a22 100644 --- a/packages/app/features/activity/RecentActivity.tsx +++ b/packages/app/features/activity/RecentActivity.tsx @@ -103,12 +103,19 @@ function ActivityFeed({ {date} - - {activities.map((activity) => ( + + {activities.map((activity, activityIndex) => ( - + onActivityPress(activity)} + btrr={activityIndex === 0 ? '$4' : 0} + btlr={activityIndex === 0 ? '$4' : 0} + bbrr={activityIndex === activities.length - 1 ? '$4' : 0} + bblr={activityIndex === activities.length - 1 ? '$4' : 0} + /> ))} diff --git a/packages/app/features/home/TokenActivityRow.tsx b/packages/app/features/home/TokenActivityRow.tsx index 2be625064..adce47172 100644 --- a/packages/app/features/home/TokenActivityRow.tsx +++ b/packages/app/features/home/TokenActivityRow.tsx @@ -1,4 +1,4 @@ -import { Paragraph, Text, XStack, YStack } from '@my/ui' +import { Paragraph, Stack, Text, XStack, type XStackProps, YStack, useIsTouchDevice } from '@my/ui' import { amountFromActivity, eventNameFromActivity, subtextFromActivity } from 'app/utils/activity' import { isSendAccountReceiveEvent, @@ -8,19 +8,59 @@ import { import { ActivityAvatar } from '../activity/ActivityAvatar' import { CommentsTime } from 'app/utils/dateHelper' import { Link } from 'solito/link' +import Swipeable from 'react-native-gesture-handler/ReanimatedSwipeable' +import type { SharedValue } from 'react-native-reanimated' import { useUser } from 'app/utils/useUser' import { useHoverStyles } from 'app/utils/useHoverStyles' +import { useRouter } from 'solito/router' +import { Reply } from '@tamagui/lucide-icons' +import { useState } from 'react' + +const ReplySendAction = ({ + recipient, + idType, + progress, +}: { recipient: string | undefined; idType: string; progress: SharedValue }) => { + const router = useRouter() + const [scale, setScale] = useState(0.0) + const [opacity, setOpacity] = useState(0.0) + //Needs a number id to avoid conflicts with other progress listeners + progress.addListener(0, (value) => { + setScale(Math.min(value, 1.0)) + setOpacity(Math.min(value, 1.0)) + }) + + const onPress = () => { + router.push({ + pathname: '/send', + query: { recipient: recipient, idType: idType }, + }) + } + return ( + + + + ) +} export function TokenActivityRow({ activity, onPress, + ...props }: { activity: Activity onPress?: (activity: Activity) => void -}) { +} & XStackProps) { const { profile } = useUser() const { created_at, from_user, to_user } = activity + const router = useRouter() const amount = amountFromActivity(activity) const date = CommentsTime(new Date(created_at)) const eventName = eventNameFromActivity(activity) @@ -28,84 +68,131 @@ export function TokenActivityRow({ const isERC20Transfer = isSendAccountTransfersEvent(activity) const isETHReceive = isSendAccountReceiveEvent(activity) const hoverStyles = useHoverStyles() + const isTouchDevice = useIsTouchDevice() + const [isSwipeTriggered, setIsSwipeTriggered] = useState(false) + const replyRecipient = + profile?.send_id === from_user?.send_id ? to_user?.send_id : from_user?.send_id + + const renderRightActions = (progress: SharedValue) => { + //Needs a number id to avoid conflicts with other progress listeners + progress.addListener(1, (value) => { + if (value > 1.1) { + setIsSwipeTriggered(true) + } else { + setIsSwipeTriggered(false) + } + }) + + return ( + + ) + } + + const handleOnSwipeableWillClose = () => { + if (isSwipeTriggered) { + router.push({ + pathname: '/send', + query: { recipient: replyRecipient, idType: 'sendid' }, + }) + } + } return ( - onPress?.(activity)} + - - - - - - {eventName} - - - {amount} - - - - {(isERC20Transfer || isETHReceive) && - Boolean(to_user?.send_id) && - Boolean(from_user?.send_id) ? ( - { - e.stopPropagation() - }} - maxWidth={'60%'} - > - onPress?.(activity)} + bc="$color1" + {...props} + > + + + + + + {eventName} + + + {amount} + + + + {(isERC20Transfer || isETHReceive) && + Boolean(to_user?.send_id) && + Boolean(from_user?.send_id) ? ( + { + e.stopPropagation() }} + maxWidth={'60%'} > - - {subtext} - - - - ) : ( - - {subtext} + + {subtext} + + + + ) : ( + + {subtext} + + )} + + {date} - )} - - {date} - - - + + + - + ) } diff --git a/packages/app/features/home/TokenDetails.tsx b/packages/app/features/home/TokenDetails.tsx index 4fefcf70d..5fa9f7ce6 100644 --- a/packages/app/features/home/TokenDetails.tsx +++ b/packages/app/features/home/TokenDetails.tsx @@ -146,7 +146,7 @@ export const TokenDetailsMarketData = ({ coin }: { coin: CoinWithBalance }) => { ) : ( - {changePercent24h === undefined ? ( + {!changePercent24h ? ( Failed to load market data diff --git a/packages/app/features/home/TokenDetailsHistory.tsx b/packages/app/features/home/TokenDetailsHistory.tsx index 9f7254d3e..b1135cee4 100644 --- a/packages/app/features/home/TokenDetailsHistory.tsx +++ b/packages/app/features/home/TokenDetailsHistory.tsx @@ -7,8 +7,9 @@ import { AnimateEnter } from './TokenDetails' import { TokenActivityRow } from './TokenActivityRow' export const TokenDetailsHistory = ({ coin }: { coin: CoinWithBalance }) => { + const pageSize = 10 const result = useTokenActivityFeed({ - pageSize: 10, + pageSize, address: coin.token === 'eth' ? undefined : hexToBytea(coin.token), }) const { @@ -40,9 +41,12 @@ export const TokenDetailsHistory = ({ coin }: { coin: CoinWithBalance }) => { default: { let lastDate: string | undefined return ( - - {pages?.map((activities) => { - return activities.map((activity) => { + + {pages?.map((activities, pageIndex) => { + return activities.map((activity, activityIndex) => { + const isFirst = pageIndex === 0 && activityIndex === 0 + const isLastPage = pageIndex === pages.length - 1 + const isLast = isLastPage && activityIndex === activities.length - 1 const date = activity.created_at.toLocaleDateString() const isNewDate = !lastDate || date !== lastDate if (isNewDate) { @@ -53,7 +57,13 @@ export const TokenDetailsHistory = ({ coin }: { coin: CoinWithBalance }) => { key={`${activity.event_name}-${activity.created_at}-${activity?.from_user?.id}-${activity?.to_user?.id}`} > - + ) diff --git a/packages/app/features/home/__snapshots__/TokenDetails.test.tsx.snap b/packages/app/features/home/__snapshots__/TokenDetails.test.tsx.snap index 943c179b1..872fbcd65 100644 --- a/packages/app/features/home/__snapshots__/TokenDetails.test.tsx.snap +++ b/packages/app/features/home/__snapshots__/TokenDetails.test.tsx.snap @@ -284,16 +284,12 @@ exports[`TokenDetails 1`] = ` + + + - - - + strokeLinecap={1} + strokeLinejoin={1} + strokeWidth="2" + > + + + + + + + + + @@ -440,6 +586,7 @@ exports[`TokenDetails 1`] = ` "borderTopLeftRadius": 9, "borderTopRightRadius": 9, "flexDirection": "column", + "gap": 7, "height": 48, "justifyContent": "center", "maxHeight": 48, @@ -482,104 +629,172 @@ exports[`TokenDetails 1`] = ` } /> - - - - - - - Withdraw - - - 10 USDC - - - - + + + + + + + + - 0x93F...761a - - - 7 mon ago - + > + + Withdraw + + + 10 USDC + + + + + 0x93F...761a + + + 7 mon ago + + + @@ -612,108 +827,258 @@ exports[`TokenDetails 1`] = ` } > + + + - - - + strokeLinecap={1} + strokeLinejoin={1} + strokeWidth="2" + > + + + + + + + + + @@ -726,6 +1091,7 @@ exports[`TokenDetails 1`] = ` "borderTopLeftRadius": 9, "borderTopRightRadius": 9, "flexDirection": "column", + "gap": 7, "height": 48, "justifyContent": "center", "maxHeight": 48, @@ -768,104 +1134,172 @@ exports[`TokenDetails 1`] = ` } /> - - - - - - - Deposit - - - 20 USDC - - - - + + + + + + + + - 0xa71...0000 - - - 7 mon ago - + > + + Deposit + + + 20 USDC + + + + + 0xa71...0000 + + + 7 mon ago + + + @@ -898,41 +1332,62 @@ exports[`TokenDetails 1`] = ` } > + + + @@ -948,74 +1403,203 @@ exports[`TokenDetails 1`] = ` onPressOut={[Function]} style={ { - "flexDirection": "row", + "alignItems": "flex-end", + "backgroundColor": "#081619", + "justifyContent": "center", + "paddingLeft": 18, + "paddingRight": 2, } } > - - - - + + + + + + + + + @@ -1028,6 +1612,7 @@ exports[`TokenDetails 1`] = ` "borderTopLeftRadius": 9, "borderTopRightRadius": 9, "flexDirection": "column", + "gap": 7, "height": 48, "justifyContent": "center", "maxHeight": 48, @@ -1059,7 +1644,7 @@ exports[`TokenDetails 1`] = ` onLoad={[Function]} source={ { - "uri": "https://ui-avatars.com/api/?name=Alice&size=256&format=png&background=86ad7f", + "uri": "https://i.pravatar.cc/500?u=alice", } } style={ @@ -1070,100 +1655,184 @@ exports[`TokenDetails 1`] = ` } /> + + + + + + + - - - - - - Received - - - 30 USDC - - - + + Received + + + 30 USDC + + + + + + + /alice + + + - /alice + 7 mon ago - - 7 mon ago - diff --git a/packages/app/package.json b/packages/app/package.json index 0444c9c30..c45817807 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -55,6 +55,7 @@ "permissionless": "^0.1.14", "react-hook-form": "^7.48.2", "react-native": "0.74.1", + "react-native-gesture-handler": "^2.22.0", "react-native-safe-area-context": "4.8.2", "react-native-svg": "15.2.0", "react-use-precision-timer": "^3.5.5", diff --git a/packages/app/utils/distributions.ts b/packages/app/utils/distributions.ts index 5c634646e..bc25c3ecb 100644 --- a/packages/app/utils/distributions.ts +++ b/packages/app/utils/distributions.ts @@ -1,4 +1,4 @@ -import type { Database, Json, Tables } from '@my/supabase/database.types' +import type { Database, Tables } from '@my/supabase/database.types' import { baseMainnetBundlerClient, baseMainnetClient, diff --git a/yarn.lock b/yarn.lock index 95012fb6f..fef7ab242 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14033,6 +14033,7 @@ __metadata: permissionless: "npm:^0.1.14" react-hook-form: "npm:^7.48.2" react-native: "npm:0.74.1" + react-native-gesture-handler: "npm:^2.22.0" react-native-safe-area-context: "npm:4.8.2" react-native-svg: "npm:15.2.0" react-test-renderer: "npm:^18.3.1" @@ -19824,7 +19825,7 @@ __metadata: react-native: "npm:0.74.1" react-native-clean-project: "npm:^4.0.3" react-native-dotenv: "npm:^3.4.9" - react-native-gesture-handler: "npm:~2.14.1" + react-native-gesture-handler: "npm:^2.22.0" react-native-ios-modal: "npm:^0.1.8" react-native-reanimated: "npm:~3.10.1" react-native-safe-area-context: "npm:4.8.2" @@ -29597,19 +29598,17 @@ __metadata: languageName: node linkType: hard -"react-native-gesture-handler@npm:~2.14.1": - version: 2.14.1 - resolution: "react-native-gesture-handler@npm:2.14.1" +"react-native-gesture-handler@npm:^2.22.0": + version: 2.22.0 + resolution: "react-native-gesture-handler@npm:2.22.0" dependencies: "@egjs/hammerjs": "npm:^2.0.17" hoist-non-react-statics: "npm:^3.3.0" invariant: "npm:^2.2.4" - lodash: "npm:^4.17.21" - prop-types: "npm:^15.7.2" peerDependencies: react: "*" react-native: "*" - checksum: 10/0a86209d515febf6da489c210f8983ded4a8d6e87dc94bca55f8afab657ec096797b21954b7599f6e05e1238634d770b5462266f3f4018363e4eb73496f2147a + checksum: 10/8c428bf6c6b5bf0752085d5e5a1659af687ca34962c17019d289cac66573141ee76b0d0a681b5276688068ced69999304455c99d22da28aca8b1c6c606c1af92 languageName: node linkType: hard