Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 10 additions & 1 deletion src/Components/Collectibles/CollectiblesSendActionButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, { useCallback } from "react"
import { useI18nContext } from "~i18n"
import { Routes } from "~Navigation"
import { CollectiblesActionButton } from "./CollectiblesActionButton"
import { useFeatureFlags } from "~Components"

type Props = {
address: string
Expand All @@ -13,14 +14,22 @@ export const CollectiblesSendActionButton = ({ address, tokenId, onClose }: Prop
const { LL } = useI18nContext()

const nav = useNavigation()
const { betterWorldFeature } = useFeatureFlags()

const onPress = useCallback(async () => {
onClose()
if (betterWorldFeature.balanceScreen?.send?.enabled) {
nav.navigate(Routes.SEND_NFT, {
contractAddress: address,
tokenId,
})
return
}
nav.navigate(Routes.INSERT_ADDRESS_SEND, {
contractAddress: address,
tokenId,
})
}, [address, nav, onClose, tokenId])
}, [address, nav, onClose, tokenId, betterWorldFeature.balanceScreen?.send?.enabled])

return (
<CollectiblesActionButton
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React, { PropsWithChildren, useCallback, useContext, useMemo, useState } from "react"
import { SharedValue, useSharedValue } from "react-native-reanimated"
import { FungibleTokenWithBalance } from "~Model"
import { FungibleTokenWithBalance, NonFungibleToken } from "~Model"

export type SendFlowStep = "insertAddress" | "selectAmount" | "summary"

type SendFlowState = {
export type SendFlowState = {
token?: FungibleTokenWithBalance
nft?: NonFungibleToken
amount?: string
fiatAmount?: string
address?: string
Expand All @@ -17,7 +18,7 @@ type SendFlowState = {
amountInFiat?: boolean
}

type SendContextType = {
export type SendContextType = {
flowState: SendFlowState
setFlowState: React.Dispatch<React.SetStateAction<SendFlowState>>
step: SendFlowStep
Expand All @@ -33,7 +34,7 @@ type SendContextType = {
setTxError: (hasError: boolean) => void
}

const SendContext = React.createContext<SendContextType | undefined>(undefined)
export const SendContext = React.createContext<SendContextType | undefined>(undefined)

type SendContextProviderProps = PropsWithChildren<{
initialToken?: FungibleTokenWithBalance
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import React from "react"
import { renderHook, act } from "@testing-library/react-hooks"
import { TestWrapper, TestHelpers } from "~Test"
import { SendNFTContextProvider } from "./SendNFTContextProvider"
import { useSendContext } from "./SendContextProvider"
import { RootState } from "~Storage/Redux/Types"

const { NFT_Mock } = TestHelpers.data

const createWrapper = (preloadedState: Partial<RootState>, initialNft?: typeof NFT_Mock) => {
return ({ children }: { children: React.ReactNode }) => (
<TestWrapper preloadedState={preloadedState}>
<SendNFTContextProvider initialNft={initialNft}>{children}</SendNFTContextProvider>
</TestWrapper>
)
}

describe("SendNFTContextProvider", () => {
it("should render with initial state", () => {
const { result } = renderHook(() => useSendContext(), {
wrapper: createWrapper({}),
})

expect(result.current.flowState).toMatchObject({
nft: undefined,
address: "",
})
expect(result.current.step).toBe("insertAddress")
})

it("should render with initial NFT", () => {
const { result } = renderHook(() => useSendContext(), {
wrapper: createWrapper({}, NFT_Mock),
})

expect(result.current.flowState.nft).not.toBeUndefined()
expect(result.current.flowState.nft?.tokenId).toEqual(NFT_Mock.tokenId)
expect(result.current.flowState.nft?.address).toEqual(NFT_Mock.address)
})

it("should update the flow state", () => {
const { result } = renderHook(() => useSendContext(), {
wrapper: createWrapper({}, NFT_Mock),
})

act(() => {
result.current.setFlowState(prev => ({
...prev,
address: "0x1234567890123456789012345678901234567890",
}))
})

expect(result.current.flowState.address).toBe("0x1234567890123456789012345678901234567890")
})

it("should navigate from insertAddress to summary", () => {
const { result } = renderHook(() => useSendContext(), {
wrapper: createWrapper({}),
})

expect(result.current.step).toBe("insertAddress")

act(() => {
result.current.goToNext()
})

expect(result.current.step).toBe("summary")
})

it("should navigate from summary back to insertAddress", () => {
const { result } = renderHook(() => useSendContext(), {
wrapper: createWrapper({}),
})

expect(result.current.step).toBe("insertAddress")

act(() => {
result.current.goToNext()
})

expect(result.current.step).toBe("summary")

act(() => {
result.current.goToPrevious()
})

expect(result.current.step).toBe("insertAddress")
})

it("should not change step when goToPrevious is called on insertAddress", () => {
const { result } = renderHook(() => useSendContext(), {
wrapper: createWrapper({}),
})

expect(result.current.step).toBe("insertAddress")

act(() => {
result.current.goToPrevious()
})

expect(result.current.step).toBe("insertAddress")
})

it("should not change step when goToNext is called on summary", () => {
const { result } = renderHook(() => useSendContext(), {
wrapper: createWrapper({}),
})

act(() => {
result.current.goToNext()
})

expect(result.current.step).toBe("summary")

act(() => {
result.current.goToNext()
})

expect(result.current.step).toBe("summary")
})

it("should update the isNextButtonEnabled state", () => {
const { result } = renderHook(() => useSendContext(), {
wrapper: createWrapper({}),
})

expect(result.current.isNextButtonEnabled).toBe(true)

act(() => {
result.current.setIsNextButtonEnabled(false)
})

expect(result.current.isNextButtonEnabled).toBe(false)
})

it("should update the isPreviousButtonEnabled state", () => {
const { result } = renderHook(() => useSendContext(), {
wrapper: createWrapper({}),
})

expect(result.current.isPreviousButtonEnabled).toBe(true)

act(() => {
result.current.setIsPreviousButtonEnabled(false)
})

expect(result.current.isPreviousButtonEnabled).toBe(false)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React, { PropsWithChildren, useCallback, useMemo, useState } from "react"
import { useSharedValue } from "react-native-reanimated"
import { NonFungibleToken } from "~Model"
import { SendContext, SendContextType, SendFlowState, SendFlowStep } from "./SendContextProvider"

type SendNFTFlowStep = "insertAddress" | "summary"

type SendNFTContextProviderProps = PropsWithChildren<{
initialNft?: NonFungibleToken
}>

export const SendNFTContextProvider = ({ children, initialNft }: SendNFTContextProviderProps) => {
const [step, setStep] = useState<SendNFTFlowStep>("insertAddress")
const [flowState, setFlowState] = useState<SendFlowState>({
nft: initialNft,
address: "",
})

const [isNextButtonEnabled, setIsNextButtonEnabled] = useState(true)
const [isPreviousButtonEnabled, setIsPreviousButtonEnabled] = useState(true)

const previousStep = useSharedValue<SendFlowStep | undefined>(undefined)
const nextStep = useSharedValue<SendFlowStep | undefined>(undefined)

const goToNext = useCallback(() => {
switch (step) {
case "insertAddress":
nextStep.value = "summary"
previousStep.value = step
setStep("summary")
break
}
}, [nextStep, previousStep, step])

const goToPrevious = useCallback(() => {
switch (step) {
case "summary":
nextStep.value = "insertAddress"
previousStep.value = step
setStep("insertAddress")
break
}
}, [nextStep, previousStep, step])

const contextValue: SendContextType = useMemo(
() => ({
flowState,
setFlowState,
step,
previousStep,
nextStep,
isNextButtonEnabled,
isPreviousButtonEnabled,
setIsNextButtonEnabled,
setIsPreviousButtonEnabled,
goToNext,
goToPrevious,
}),
[
flowState,
setFlowState,
step,
previousStep,
nextStep,
isNextButtonEnabled,
isPreviousButtonEnabled,
setIsNextButtonEnabled,
setIsPreviousButtonEnabled,
goToNext,
goToPrevious,
],
)

return <SendContext.Provider value={contextValue}>{children}</SendContext.Provider>
}
1 change: 1 addition & 0 deletions src/Components/Reusable/Send/Provider/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./SendContextProvider"
export * from "./SendNFTContextProvider"
9 changes: 6 additions & 3 deletions src/Constants/Theme/Colors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,8 +381,9 @@ export type Colors = {
tokenSelectorBorder: string
tokenSelectorText: string
disabledTokenCardBackground: string
stepText: string
}
stepText: string
stepBackground: string
}
transak: string
}
Expand Down Expand Up @@ -895,8 +896,9 @@ const light: Colors = {
tokenSelectorBorder: COLORS.GREY_100,
tokenSelectorText: COLORS.GREY_700,
disabledTokenCardBackground: COLORS.GREY_100,
stepText: COLORS.GREY_500,
},
stepText: COLORS.GREY_700,
stepBackground: COLORS.GREY_200,
},
transak: COLORS.WHITE,
}
Expand Down Expand Up @@ -1284,8 +1286,9 @@ const dark: Colors = {
tokenSelectorBorder: COLORS.DARK_PURPLE_DISABLED,
tokenSelectorText: COLORS.GREY_100,
disabledTokenCardBackground: COLORS.PURPLE_DISABLED,
stepText: COLORS.GREY_300,
},
stepText: COLORS.GREY_100,
stepBackground: COLORS.PURPLE_DISABLED,
},
transak: COLORS.TRANSAK_DARK,
}
Expand Down
8 changes: 8 additions & 0 deletions src/Navigation/Stacks/HomeStack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {
WalletDetailScreen,
WalletManagementScreen,
SendScreen,
SendNFTScreen,
} from "~Screens"
import { AppsSearchScreen } from "~Screens/Flows/App/AppsScreen"
import { AssetDetailScreenSheet } from "~Screens/Flows/App/AssetDetailScreenSheet"
Expand Down Expand Up @@ -182,6 +183,10 @@ export type RootStackParamListHome = {
nftAddress: string
transactionClauses: TransactionClause[]
}
[Routes.SEND_NFT]: {
contractAddress: string
tokenId: string
}
[Routes.SEND_NFT_RECAP]: {
contractAddress: string
tokenId: string
Expand Down Expand Up @@ -392,6 +397,9 @@ export const HomeStack = () => {
options={{ headerShown: false }}
/>
)}
{betterWorldFeature.balanceScreen.collectibles.enabled && (
<Screen name={Routes.SEND_NFT} component={SendNFTScreen} options={{ headerShown: false }} />
)}
{betterWorldFeature.balanceScreen.collectibles.enabled && (
<Screen
name={Routes.SEND_NFT_RECAP}
Expand Down
8 changes: 8 additions & 0 deletions src/Navigation/Stacks/NFTStack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
NFTScreen,
ReportNFTTransactionScreen,
SendNFTRecapScreen,
SendNFTScreen,
WalletDetailScreen,
WalletManagementScreen,
} from "~Screens"
Expand All @@ -30,6 +31,11 @@ export type RootStackParamListNFT = {
tokenId?: string
}

[Routes.SEND_NFT]: {
contractAddress: string
tokenId: string
}

[Routes.SEND_NFT_RECAP]: {
contractAddress: string
tokenId: string
Expand Down Expand Up @@ -75,6 +81,8 @@ export const NFTStack = () => {
options={{ headerShown: false }}
/>

<Screen name={Routes.SEND_NFT} component={SendNFTScreen} options={{ headerShown: false }} />

<Screen name={Routes.SEND_NFT_RECAP} component={SendNFTRecapScreen} options={{ headerShown: false }} />

<Screen
Expand Down
Loading