diff --git a/jestSetup.js b/jestSetup.js
index c6254c92090..d56ef11dc76 100644
--- a/jestSetup.js
+++ b/jestSetup.js
@@ -254,3 +254,8 @@ jest.mock('react-native-device-info', () => {
getVersion: jest.fn()
}
})
+
+jest.mock('react-native-reorderable-list', () => ({
+ ...jest.requireActual('react-native-reorderable-list'),
+ useReorderableDrag: () => jest.fn()
+}))
diff --git a/package.json b/package.json
index 398dab4006b..5c764df1245 100644
--- a/package.json
+++ b/package.json
@@ -123,7 +123,6 @@
"react-native-contacts": "^7.0.4",
"react-native-custom-tabs": "https://github.com/adminphoeniixx/react-native-custom-tabs#develop",
"react-native-device-info": "^13.2.0",
- "react-native-draggable-flatlist": "^4.0.1",
"react-native-email-link": "^1.14.5",
"react-native-fast-image": "^8.5.11",
"react-native-fast-shadow": "^0.1.0",
@@ -145,6 +144,7 @@
"react-native-permissions": "^4.1.5",
"react-native-piratechain": "^0.5.4",
"react-native-reanimated": "^3.14.0",
+ "react-native-reorderable-list": "^0.5.0",
"react-native-safari-view": "^2.1.0",
"react-native-safe-area-context": "^4.10.1",
"react-native-screens": "^3.31.1",
diff --git a/src/__tests__/components/WalletListSortableRow.test.tsx b/src/__tests__/components/WalletListSortableRow.test.tsx
index 4e29da2f1ad..c233dcea666 100644
--- a/src/__tests__/components/WalletListSortableRow.test.tsx
+++ b/src/__tests__/components/WalletListSortableRow.test.tsx
@@ -10,7 +10,7 @@ describe('WalletListSortableRow', () => {
it('should render with loading wallet', () => {
const renderer = TestRenderer.create(
- {}} />
+
)
expect(renderer.toJSON()).toMatchSnapshot()
@@ -48,7 +48,7 @@ describe('WalletListSortableRow', () => {
const renderer = TestRenderer.create(
- {}} />
+
)
expect(renderer.toJSON()).toMatchSnapshot()
diff --git a/src/__tests__/components/__snapshots__/WalletListSortableRow.test.tsx.snap b/src/__tests__/components/__snapshots__/WalletListSortableRow.test.tsx.snap
index 40dc0764af4..c62822efe4d 100644
--- a/src/__tests__/components/__snapshots__/WalletListSortableRow.test.tsx.snap
+++ b/src/__tests__/components/__snapshots__/WalletListSortableRow.test.tsx.snap
@@ -2,48 +2,54 @@
exports[`WalletListSortableRow should render with fake wallet 1`] = `
-
+
@@ -79,199 +85,199 @@ exports[`WalletListSortableRow should render with fake wallet 1`] = `
+
+
-
-
-
+ }
+ />
+
+
-
-
- FAKE
-
-
- 0
-
-
-
+
-
- Test wallet
-
-
- $0.00
-
-
+ 0
+
+
+
+
+ Test wallet
+
+
+ $0.00
+
diff --git a/src/components/themed/WalletListSortable.tsx b/src/components/themed/WalletListSortable.tsx
index 735a87759be..7addec38a04 100644
--- a/src/components/themed/WalletListSortable.tsx
+++ b/src/components/themed/WalletListSortable.tsx
@@ -1,19 +1,16 @@
import { EdgeWalletStates } from 'edge-core-js'
import * as React from 'react'
-import DraggableFlatList, { DragEndParams, RenderItemParams, ScaleDecorator } from 'react-native-draggable-flatlist'
-import Animated from 'react-native-reanimated'
+import ReorderableList, { ReorderableListItem } from 'react-native-reorderable-list'
import { SCROLL_INDICATOR_INSET_FIX } from '../../constants/constantSettings'
import { useHandler } from '../../hooks/useHandler'
import { useWatch } from '../../hooks/useWatch'
-import { useSceneScrollHandler } from '../../state/SceneScrollState'
+import { useSceneScrollWorkletHandler } from '../../state/SceneScrollState'
import { useSelector } from '../../types/reactRedux'
import { InsetStyle } from '../common/SceneWrapper'
import { showError } from '../services/AirshipInstance'
import { WalletListSortableRow } from './WalletListSortableRow'
-const AnimatedDraggableFlatList = Animated.createAnimatedComponent(DraggableFlatList)
-
interface Props {
insetStyle?: InsetStyle
}
@@ -23,39 +20,50 @@ interface Props {
*/
export function WalletListSortable(props: Props) {
const { insetStyle } = props
+
// Subscribe to account state:
const account = useSelector(state => state.core.account)
const currencyWallets = useWatch(account, 'currencyWallets')
const [walletOrder, setWalletOrder] = React.useState(account.activeWalletIds)
- const handleDragEnd = useHandler((params: DragEndParams) => setWalletOrder(params.data))
- const keyExtractor = useHandler((walletId: string) => walletId)
- const renderItem = useHandler((params: RenderItemParams) => (
-
-
-
- ))
- const handleScroll = useSceneScrollHandler()
+ const handleReorder = useHandler(({ from, to }: { from: number; to: number }) => {
+ // Reorder the walletOrder array
+ const newOrder = [...walletOrder]
+ newOrder.splice(to, 0, newOrder.splice(from, 1)[0])
+ setWalletOrder(newOrder)
- React.useEffect(() => () => {
+ // Update the wallet sort order in the account
const keyStates: EdgeWalletStates = {}
- for (let i = 0; i < walletOrder.length; ++i) {
- const walletId = walletOrder[i]
+ for (let i = 0; i < newOrder.length; ++i) {
+ const walletId = newOrder[i]
keyStates[walletId] = { sortIndex: i }
}
account.changeWalletStates(keyStates).catch(error => showError(error))
})
+ const keyExtractor = React.useCallback(
+ (item: string) => item,
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ [walletOrder]
+ )
+
+ const renderItem = useHandler(item => {
+ return (
+
+
+
+ )
+ })
+
+ const handleScroll = useSceneScrollWorkletHandler()
+
return (
- void
}
function WalletListSortableRowComponent(props: Props) {
- const { wallet, onDrag } = props
+ const { wallet } = props
+
+ const handleDrag = useReorderableDrag()
const theme = useTheme()
const styles = getStyles(theme)
@@ -36,7 +38,7 @@ function WalletListSortableRowComponent(props: Props) {
if (wallet == null || exchangeDenomination == null) {
return (
-
+
@@ -62,26 +64,26 @@ function WalletListSortableRowComponent(props: Props) {
const fiatBalanceString = showBalance ? formatNumber(fiatBalanceFormat, { toFixed: FIAT_PRECISION }) : ''
return (
-
-
+
+
-
-
+
+
+
+
+
+
+ {currencyCode}
+ {finalCryptoAmountString}
-
-
- {currencyCode}
- {finalCryptoAmountString}
-
-
- {name}
- {fiatBalanceSymbol + fiatBalanceString}
-
+
+ {name}
+ {fiatBalanceSymbol + fiatBalanceString}
-
+
)
}
@@ -89,6 +91,10 @@ const getStyles = cacheStyles((theme: Theme) => ({
container: {
paddingHorizontal: theme.rem(1)
},
+ handleContainer: {
+ margin: -theme.rem(0.5),
+ padding: theme.rem(0.5)
+ },
rowContainer: {
flexDirection: 'row',
justifyContent: 'center',
diff --git a/src/state/SceneScrollState.tsx b/src/state/SceneScrollState.tsx
index 836c2944ac8..adb2b7109de 100644
--- a/src/state/SceneScrollState.tsx
+++ b/src/state/SceneScrollState.tsx
@@ -1,7 +1,7 @@
import { useIsFocused } from '@react-navigation/native'
import { useMemo } from 'react'
import { NativeScrollEvent, NativeSyntheticEvent } from 'react-native'
-import { SharedValue, useAnimatedReaction, useAnimatedScrollHandler, useSharedValue } from 'react-native-reanimated'
+import { SharedValue, useAnimatedReaction, useAnimatedScrollHandler, useSharedValue, useWorkletCallback } from 'react-native-reanimated'
import { createStateProvider } from './createStateProvider'
@@ -113,3 +113,39 @@ export const useSceneScrollHandler = (): SceneScrollHandler => {
return handler
}
+
+/** Like `useSceneScrollHandler`, but specifically for worklets that need a
+ * `(event: NativeScrollEvent) => void` type prop */
+export const useSceneScrollWorkletHandler = () => {
+ const scrollY = useSceneScrollContext(state => state.scrollY)
+
+ // Create shared values for scroll position
+ const isFocused = useIsFocused()
+
+ const localScrollY = useSharedValue(0)
+
+ useAnimatedReaction(
+ () => {
+ return isFocused
+ },
+ isFocusedResult => {
+ if (isFocusedResult && localScrollY.value !== scrollY.value) {
+ scrollY.value = localScrollY.value
+ }
+ }
+ )
+
+ // Define the handleScroll function as a worklet using useWorkletCallback
+ const handleScroll = useWorkletCallback((event: NativeScrollEvent) => {
+ 'worklet'
+ if (!isFocused) return
+
+ const y = event.contentOffset.y
+ if (scrollY.value !== y) {
+ localScrollY.value = y
+ scrollY.value = y
+ }
+ }, [])
+
+ return handleScroll
+}
diff --git a/yarn.lock b/yarn.lock
index 8346fcc5d1d..6c0179b83c6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1100,7 +1100,7 @@
"@babel/types" "^7.4.4"
esutils "^2.0.2"
-"@babel/preset-typescript@^7.13.0", "@babel/preset-typescript@^7.16.7", "@babel/preset-typescript@^7.17.12", "@babel/preset-typescript@^7.18.6":
+"@babel/preset-typescript@^7.13.0", "@babel/preset-typescript@^7.16.7", "@babel/preset-typescript@^7.18.6":
version "7.24.1"
resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.24.1.tgz#89bdf13a3149a17b3b2a2c9c62547f06db8845ec"
integrity sha512-1DBaMmRDpuYQBPWD8Pf/WEwCrtgRHxsZnP4mIy9G/X+hFfbI47Q2G4t1Paakld84+qsk2fSsUPMKg71jkoOOaQ==
@@ -16423,13 +16423,6 @@ react-native-device-info@^13.2.0:
resolved "https://registry.yarnpkg.com/react-native-device-info/-/react-native-device-info-13.2.0.tgz#2b4026daf59dd08d82b6d65b63c18cb26f310ed2"
integrity sha512-VpTxHZsEZ7kes2alaZkB31278KuSPXfTZ4TmCCN77+bYxNnaHUDiBiQ1TSoKAOp51b7gZ/7EvM4McfgHofcTBQ==
-react-native-draggable-flatlist@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/react-native-draggable-flatlist/-/react-native-draggable-flatlist-4.0.1.tgz#2f027d387ba4b8f3eb0907340e32cb85e6460df2"
- integrity sha512-ZO1QUTNx64KZfXGXeXcBfql67l38X7kBcJ3rxUVZzPHt5r035GnGzIC0F8rqSXp6zgnwgUYMfB6zQc5PKmPL9Q==
- dependencies:
- "@babel/preset-typescript" "^7.17.12"
-
react-native-email-link@^1.14.5:
version "1.14.5"
resolved "https://registry.yarnpkg.com/react-native-email-link/-/react-native-email-link-1.14.5.tgz#05e13002bd850ebaf98477b4cfbfdfa972a57608"
@@ -16581,6 +16574,11 @@ react-native-reanimated@^3.14.0:
convert-source-map "^2.0.0"
invariant "^2.2.4"
+react-native-reorderable-list@^0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/react-native-reorderable-list/-/react-native-reorderable-list-0.5.0.tgz#5f85360d68988fdd350cea720b0201f413329101"
+ integrity sha512-p830KvMJP8r2nOsdEyGlNW6F1/CA4bvCTCoFsZR+HJ3RFjujHIX3C8tf+Sl75wFGBUlPd5SSuJMZ0t85wUJYZA==
+
react-native-safari-view@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/react-native-safari-view/-/react-native-safari-view-2.1.0.tgz#1e0cd12c62bce79bc1759c7e281646b08b61c959"