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

chore: move nfc to receive screen #2712

Merged
merged 7 commits into from
Oct 19, 2023
Merged
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
9 changes: 9 additions & 0 deletions __tests__/screens/receive.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ import { act, render } from "@testing-library/react-native"
import { ContextForScreen } from "./helper"
import ReceiveScreen from "@app/screens/receive-bitcoin-screen/receive-screen"

jest.mock("react-native-nfc-manager", () => {
return {
NfcManager: {
start: jest.fn(),
stop: jest.fn(),
},
}
})

it("Receive", async () => {
render(
<ContextForScreen>
Expand Down
78 changes: 53 additions & 25 deletions app/components/modal-nfc/modal-nfc.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,35 @@ import Icon from "react-native-vector-icons/Ionicons"
import { GaloySecondaryButton } from "../atomic/galoy-secondary-button"
import { parseDestination } from "@app/screens/send-bitcoin-screen/payment-destination"
import { logParseDestinationResult } from "@app/utils/analytics"
import { useNavigation } from "@react-navigation/native"
import { StackNavigationProp } from "@react-navigation/stack"
import { RootStackParamList } from "@app/navigation/stack-param-lists"
import { DestinationDirection } from "@app/screens/send-bitcoin-screen/payment-destination/index.types"
import {
DestinationDirection,
ReceiveDestination,
} from "@app/screens/send-bitcoin-screen/payment-destination/index.types"
import {
WalletCurrency,
useAccountDefaultWalletLazyQuery,
useScanningQrCodeScreenQuery,
} from "@app/graphql/generated"
import { useIsAuthed } from "@app/graphql/is-authed-context"
import { LNURL_DOMAINS } from "@app/config"
import { isIOS } from "@rneui/base"
import {
MoneyAmount,
WalletAmount,
toBtcMoneyAmount,
toUsdMoneyAmount,
} from "@app/types/amounts"
import { usePriceConversion } from "@app/hooks"

export const ModalNfc: React.FC<{
isActive: boolean
setIsActive: (arg: boolean) => void
}> = ({ isActive, setIsActive }) => {
settlementAmount?: WalletAmount<WalletCurrency>
receiveViaNFC: (
destination: ReceiveDestination,
settlementAmount: MoneyAmount<"BTC">,
) => Promise<void>
}> = ({ isActive, setIsActive, settlementAmount, receiveViaNFC }) => {
const { data } = useScanningQrCodeScreenQuery({ skip: !useIsAuthed() })
const wallets = data?.me?.defaultAccount.wallets
const bitcoinNetwork = data?.globals?.network
Expand All @@ -33,10 +46,6 @@ export const ModalNfc: React.FC<{
fetchPolicy: "no-cache",
})

// FIXME: navigation destination?
const navigation =
useNavigation<StackNavigationProp<RootStackParamList, "sendBitcoinDestination">>()

const styles = useStyles()
const {
theme: { colors },
Expand All @@ -49,8 +58,23 @@ export const ModalNfc: React.FC<{
NfcManager.cancelTechnologyRequest()
}, [setIsActive])

const { convertMoneyAmount } = usePriceConversion()

React.useEffect(() => {
if (!LL || !wallets || !bitcoinNetwork || !isActive) {
if (isActive && !settlementAmount) {
Alert.alert(LL.ReceiveScreen.enterAmountFirst())
setIsActive(false)
return
}

if (
!LL ||
!wallets ||
!bitcoinNetwork ||
!isActive ||
!receiveViaNFC ||
!settlementAmount
) {
return
}

Expand Down Expand Up @@ -122,38 +146,42 @@ export const ModalNfc: React.FC<{
})
logParseDestinationResult(destination)

if (destination.valid) {
if (destination.valid && settlementAmount && convertMoneyAmount) {
if (destination.destinationDirection === DestinationDirection.Send) {
Alert.alert(LL.SettingsScreen.nfcOnlyReceive())
} else {
navigation.reset({
routes: [
{
name: "Primary",
},
{
name: "redeemBitcoinDetail",
params: {
receiveDestination: destination,
},
},
],
})
let amount = settlementAmount.amount
if (settlementAmount.currency === WalletCurrency.Usd) {
amount = convertMoneyAmount(
toUsdMoneyAmount(settlementAmount.amount),
WalletCurrency.Btc,
).amount
}

destination.validDestination.minWithdrawable = amount * 1000 // coz msats
destination.validDestination.maxWithdrawable = amount * 1000 // coz msats

receiveViaNFC(destination, toBtcMoneyAmount(settlementAmount.amount))
}
}

dismiss()
}

init()
// Necessary because receiveViaNFC gets rerendered at useReceiveBitcoin
// And rerendering that shouldn't cause this useEffect to retrigger
// eslint-disable-next-line react-hooks/exhaustive-deps
sandipndev marked this conversation as resolved.
Show resolved Hide resolved
}, [
LL,
wallets,
bitcoinNetwork,
accountDefaultWalletQuery,
navigation,
isActive,
dismiss,
settlementAmount,
setIsActive,
convertMoneyAmount,
])

return (
Expand Down
8 changes: 0 additions & 8 deletions app/graphql/generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1262,8 +1262,6 @@ export type Query = {
readonly __typename: 'Query';
readonly accountDefaultWallet: PublicWallet;
readonly beta: Scalars['Boolean']['output'];
/** @deprecated Deprecated in favor of realtimePrice */
readonly btcPrice?: Maybe<Price>;
readonly btcPriceList?: Maybe<ReadonlyArray<Maybe<PricePoint>>>;
readonly businessMapMarkers?: Maybe<ReadonlyArray<Maybe<MapMarker>>>;
readonly colorScheme: Scalars['String']['output'];
Expand Down Expand Up @@ -1299,11 +1297,6 @@ export type QueryAccountDefaultWalletArgs = {
};


export type QueryBtcPriceArgs = {
currency?: Scalars['DisplayCurrency']['input'];
};


export type QueryBtcPriceListArgs = {
range: PriceGraphRange;
};
Expand Down Expand Up @@ -7619,7 +7612,6 @@ export type PublicWalletResolvers<ContextType = any, ParentType extends Resolver
export type QueryResolvers<ContextType = any, ParentType extends ResolversParentTypes['Query'] = ResolversParentTypes['Query']> = {
accountDefaultWallet?: Resolver<ResolversTypes['PublicWallet'], ParentType, ContextType, RequireFields<QueryAccountDefaultWalletArgs, 'username'>>;
beta?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>;
btcPrice?: Resolver<Maybe<ResolversTypes['Price']>, ParentType, ContextType, RequireFields<QueryBtcPriceArgs, 'currency'>>;
btcPriceList?: Resolver<Maybe<ReadonlyArray<Maybe<ResolversTypes['PricePoint']>>>, ParentType, ContextType, RequireFields<QueryBtcPriceListArgs, 'range'>>;
businessMapMarkers?: Resolver<Maybe<ReadonlyArray<Maybe<ResolversTypes['MapMarker']>>>, ParentType, ContextType>;
colorScheme?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
Expand Down
1 change: 1 addition & 0 deletions app/i18n/en/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,7 @@ const en: BaseTranslation = {
title: "Home",
},
ReceiveScreen: {
enterAmountFirst: "Please enter an amount first",
activateNotifications:
"Do you want to activate notifications to be notified when the payment has arrived?",
copyClipboard: "Invoice has been copied in the clipboard",
Expand Down
8 changes: 8 additions & 0 deletions app/i18n/i18n-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1616,6 +1616,10 @@ type RootTranslation = {
title: string
}
ReceiveScreen: {
/**
* P​l​e​a​s​e​ ​e​n​t​e​r​ ​a​n​ ​a​m​o​u​n​t​ ​f​i​r​s​t
*/
enterAmountFirst: string
/**
* D​o​ ​y​o​u​ ​w​a​n​t​ ​t​o​ ​a​c​t​i​v​a​t​e​ ​n​o​t​i​f​i​c​a​t​i​o​n​s​ ​t​o​ ​b​e​ ​n​o​t​i​f​i​e​d​ ​w​h​e​n​ ​t​h​e​ ​p​a​y​m​e​n​t​ ​h​a​s​ ​a​r​r​i​v​e​d​?
*/
Expand Down Expand Up @@ -5381,6 +5385,10 @@ export type TranslationFunctions = {
title: () => LocalizedString
}
ReceiveScreen: {
/**
* Please enter an amount first
*/
enterAmountFirst: () => LocalizedString
/**
* Do you want to activate notifications to be notified when the payment has arrived?
*/
Expand Down
1 change: 1 addition & 0 deletions app/i18n/raw-i18n/source/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,7 @@
"title": "Home"
},
"ReceiveScreen": {
"enterAmountFirst": "Please enter an amount first",
"activateNotifications": "Do you want to activate notifications to be notified when the payment has arrived?",
"copyClipboard": "Invoice has been copied in the clipboard",
"copyClipboardBitcoin": "Bitcoin address has been copied in the clipboard",
Expand Down
30 changes: 15 additions & 15 deletions app/navigation/root-navigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,21 +81,6 @@ import {
} from "./stack-param-lists"
import { NotificationSettingsScreen } from "@app/screens/settings-screen/notifications-screen"

const useStyles = makeStyles(({ colors }) => ({
bottomNavigatorStyle: {
height: "10%",
paddingTop: 4,
backgroundColor: colors.white,
borderTopColor: colors.grey4,
},
headerStyle: {
backgroundColor: colors.white,
},
title: {
color: colors.black,
},
}))

const RootNavigator = createStackNavigator<RootStackParamList>()

export const RootStack = () => {
Expand Down Expand Up @@ -571,3 +556,18 @@ export const PrimaryNavigator = () => {
</Tab.Navigator>
)
}

const useStyles = makeStyles(({ colors }) => ({
bottomNavigatorStyle: {
height: "10%",
paddingTop: 4,
backgroundColor: colors.white,
borderTopColor: colors.grey4,
},
headerStyle: {
backgroundColor: colors.white,
},
title: {
color: colors.black,
},
}))
43 changes: 29 additions & 14 deletions app/screens/receive-bitcoin-screen/receive-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useIsAuthed } from "@app/graphql/is-authed-context"
import { useI18nContext } from "@app/i18n/i18n-react"
import { requestNotificationPermission } from "@app/utils/notifications"
import { useIsFocused, useNavigation } from "@react-navigation/native"
import React, { useEffect } from "react"
import React, { useEffect, useState } from "react"
import { TouchableOpacity, View } from "react-native"
import { testProps } from "../../utils/testProps"
import { withMyLnUpdateSub } from "./my-ln-updates-sub"
Expand All @@ -18,6 +18,7 @@ import { NoteInput } from "@app/components/note-input"
import Icon from "react-native-vector-icons/Ionicons"
import { SetLightningAddressModal } from "@app/components/set-lightning-address-modal"
import { GaloyCurrencyBubble } from "@app/components/atomic/galoy-currency-bubble"
import { ModalNfc } from "@app/components/modal-nfc"

const ReceiveScreen = () => {
const {
Expand All @@ -32,6 +33,21 @@ const ReceiveScreen = () => {

const request = useReceiveBitcoin()

const [displayReceiveNfc, setDisplayReceiveNfc] = useState(false)

useEffect(() => {
navigation.setOptions({
headerRight: () => (
<TouchableOpacity
style={styles.rotateIconHeaderRight}
onPress={() => setDisplayReceiveNfc(true)}
>
<Icon name="wifi" color={colors.black} size={25} />
</TouchableOpacity>
),
})
})

// notification permission
useEffect(() => {
let timeout: NodeJS.Timeout
Expand All @@ -45,19 +61,6 @@ const ReceiveScreen = () => {
return () => timeout && clearTimeout(timeout)
}, [isAuthed, isFocused])

useEffect(() => {
switch (request?.type) {
case Invoice.OnChain:
navigation.setOptions({ title: LL.ReceiveScreen.receiveViaOnchain() })
break
case Invoice.Lightning:
navigation.setOptions({ title: LL.ReceiveScreen.receiveViaInvoice() })
break
case Invoice.PayCode:
navigation.setOptions({ title: LL.ReceiveScreen.receiveViaPaycode() })
}
}, [request?.type, LL.ReceiveScreen, navigation])

useEffect(() => {
if (request?.state === PaymentRequestState.Paid) {
const id = setTimeout(() => navigation.goBack(), 5000)
Expand Down Expand Up @@ -246,6 +249,13 @@ const ReceiveScreen = () => {
isVisible={request.isSetLightningAddressModalVisible}
toggleModal={request.toggleIsSetLightningAddressModalVisible}
/>

<ModalNfc
isActive={displayReceiveNfc}
setIsActive={setDisplayReceiveNfc}
settlementAmount={request.settlementAmount}
receiveViaNFC={request.receiveViaNFC}
/>
</Screen>
</>
)
Expand Down Expand Up @@ -334,6 +344,11 @@ const useStyles = makeStyles(({ colors }) => ({
fontWeight: "700",
},
btcLow: {},
rotateIconHeaderRight: {
transform: [{ rotate: "90deg" }],
marginRight: 2,
padding: 8,
},
}))

export default withMyLnUpdateSub(ReceiveScreen)
Loading
Loading