From 8f77439bc04f704ad6e280c6c99b78e276e6e548 Mon Sep 17 00:00:00 2001 From: minibits-cash Date: Fri, 1 Mar 2024 15:32:42 +0100 Subject: [PATCH] Fix check for paid donation for wallet address --- package.json | 2 +- src/models/RelaysStore.ts | 6 ++++ src/screens/Contacts/OwnName.tsx | 54 ++++++++++++++++------------- src/screens/Contacts/RandomName.tsx | 1 + src/screens/OwnKeysScreen.tsx | 8 +++-- src/screens/WalletNameScreen.tsx | 2 +- src/services/nostrService.ts | 9 +++-- src/services/walletService.ts | 2 +- 8 files changed, 50 insertions(+), 34 deletions(-) diff --git a/package.json b/package.json index d145f563..6f442f3b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "minibits_wallet", - "version": "0.1.6-beta.30", + "version": "0.1.6-beta.31", "private": true, "scripts": { "android:clean": "cd android && ./gradlew clean", diff --git a/src/models/RelaysStore.ts b/src/models/RelaysStore.ts index e5505264..3821fae7 100644 --- a/src/models/RelaysStore.ts +++ b/src/models/RelaysStore.ts @@ -11,6 +11,7 @@ import {RelayModel, Relay} from './Relay' import {log} from '../services/logService' import { MINIBITS_RELAY_URL } from '@env' import { NostrClient } from '../services' +import AppError, { Err } from '../utils/AppError' export const RelaysStoreModel = types @@ -37,6 +38,11 @@ export const RelaysStoreModel = types addRelay(relay: Relay) { const normalized = NostrClient.getNormalizedRelayUrl(relay.url) + + if(!normalized.startsWith('wss://')) { + throw new AppError(Err.VALIDATION_ERROR, 'Relay needs to communicate over secure websocket wss://', {caller: 'addRelay'}) + } + relay.url = normalized if(self.alreadyExists(relay.url)) { diff --git a/src/screens/Contacts/OwnName.tsx b/src/screens/Contacts/OwnName.tsx index 35e2ed75..4938f280 100644 --- a/src/screens/Contacts/OwnName.tsx +++ b/src/screens/Contacts/OwnName.tsx @@ -33,7 +33,7 @@ export const OwnName = observer(function (props: {navigation: any, pubkey: strin const [isQRcodeVisible, setIsQRCodeVisible] = useState(false) const [isChecked, setIsChecked] = useState(false) // const [isNameInputEnabled, setIsNameInputEnabled] = useState(true) - const [isPaidFromWallet, setIsPaidFromWallet] = useState(false) + const [isInvoicePaid, setIsInvoicePaid] = useState(false) const [isResultModalVisible, setIsResultModalVisible] = useState(false) const [resultModalInfo, setResultModalInfo] = useState< {status: TransactionStatus, message: string} | undefined @@ -76,8 +76,7 @@ export const OwnName = observer(function (props: {navigation: any, pubkey: strin interval: 2 * 1000, // every 2s to make it responsive. Total 4 min maxPolls: 120, maxErrors: 10 - }, - {unpaidInvoice: donationInvoice}) + }) .then(() => log.trace('[checkDonationPaid]', 'Polling completed')) .catch(error => log.trace('[checkDonationPaid]', error.message), @@ -93,6 +92,26 @@ export const OwnName = observer(function (props: {navigation: any, pubkey: strin }, [donationInvoice]) + useEffect(() => { + const handleIsInvoicePaid = async () => { + if(!isInvoicePaid || !donationInvoice) { + return + } + + stopPolling(`checkDonationPaidPoller-${donationInvoice.payment_hash}`) + setResultModalInfo({ + status: TransactionStatus.COMPLETED, + message: `Thank you! Donation for ${ownName+MINIBITS_NIP05_DOMAIN} has been successfully paid.` + }) + toggleResultModal() + resetState() + } + handleIsInvoicePaid() + return () => { + } + }, [isInvoicePaid]) + + const togglePaymentModal = () => setIsPaymentModalVisible(previousState => !previousState) @@ -100,18 +119,16 @@ export const OwnName = observer(function (props: {navigation: any, pubkey: strin setIsResultModalVisible(previousState => !previousState) - const resetState = function () { - // setIsNameInputEnabled(true) + const resetState = function () { setIsChecked(false) setOwnName('') setInfo('') setIsLoading(false) setIsPaymentModalVisible(false) setDonationInvoice(undefined) - setDonationAmount(DEFAULT_DONATION_AMOUNT) - setIsResultModalVisible(false) + setDonationAmount(DEFAULT_DONATION_AMOUNT) setIsQRCodeVisible(false) - setIsPaidFromWallet(false) + setIsInvoicePaid(false) } @@ -182,7 +199,6 @@ export const OwnName = observer(function (props: {navigation: any, pubkey: strin const onPayDonation = async function () { try { - setIsPaidFromWallet(true) return navigation.navigate('WalletNavigator', { screen: 'Transfer', params: { @@ -197,32 +213,22 @@ export const OwnName = observer(function (props: {navigation: any, pubkey: strin } // poll handler - const checkDonationPaid = async function (params: {unpaidInvoice: {payment_hash: string, payment_request: string}}): Promise { + const checkDonationPaid = async function (): Promise { try { - const {unpaidInvoice} = params - - if(!unpaidInvoice) { + if(!donationInvoice) { return } const { paid } = await MinibitsClient.checkDonationPaid( - unpaidInvoice.payment_hash as string, + donationInvoice.payment_hash as string, pubkey as string ) if(paid) { - setIsLoading(true) - + setIsLoading(true) await walletProfileStore.updateName(ownName) - setIsLoading(false) - setResultModalInfo({ - status: TransactionStatus.COMPLETED, - message: `Thank you! Donation for ${ownName+MINIBITS_NIP05_DOMAIN} has been successfully paid.` - }) - toggleResultModal() - togglePaymentModal() - stopPolling(`checkDonationPaidPoller-${unpaidInvoice.payment_hash}`) + setIsInvoicePaid(true) return } } catch (e: any) { diff --git a/src/screens/Contacts/RandomName.tsx b/src/screens/Contacts/RandomName.tsx index 4acac969..a809bc1d 100644 --- a/src/screens/Contacts/RandomName.tsx +++ b/src/screens/Contacts/RandomName.tsx @@ -104,6 +104,7 @@ export const RandomName = observer(function (props: {navigation: any, pubkey: st {error && } {info && } + {isLoading && } ) }) diff --git a/src/screens/OwnKeysScreen.tsx b/src/screens/OwnKeysScreen.tsx index e5f4fb6d..627fa6e9 100644 --- a/src/screens/OwnKeysScreen.tsx +++ b/src/screens/OwnKeysScreen.tsx @@ -107,8 +107,12 @@ export const OwnKeysScreen: FC = observer(function OwnKeysSc const relaysToConnect = relaysStore.allPublicUrls setOwnProfileRelays(relaysToConnect) - const profile: NostrProfile = await NostrClient.getProfileFromRelays(nip05Pubkey, relaysToConnect) - + const profile: NostrProfile | undefined = await NostrClient.getProfileFromRelays(nip05Pubkey, relaysToConnect) + + if(!profile) { + throw new AppError(Err.VALIDATION_ERROR, 'Could not retrieve profile from relays', {nip05Pubkey, relaysToConnect}) + } + // check that the profile's nip05 matches the one given by user and living on nip05 .well-known server if(!profile.nip05) { if(profile.name && profile.name.toLowerCase() === nip05Name) { diff --git a/src/screens/WalletNameScreen.tsx b/src/screens/WalletNameScreen.tsx index ca39003d..1fbac24c 100644 --- a/src/screens/WalletNameScreen.tsx +++ b/src/screens/WalletNameScreen.tsx @@ -19,7 +19,7 @@ export const WalletNameScreen: FC = observer(function Wal }) const {walletProfileStore} = useStores() - const {pubkey, name, nip05} = walletProfileStore + const {pubkey, nip05} = walletProfileStore const renderScene = ({route}: {route: Route}) => { switch (route.key) { diff --git a/src/services/nostrService.ts b/src/services/nostrService.ts index 00971c0c..882cd516 100644 --- a/src/services/nostrService.ts +++ b/src/services/nostrService.ts @@ -349,7 +349,7 @@ const getNip05PubkeyAndRelays = async function (nip05: string) { -const getProfileFromRelays = async function (pubkey: string, relays: string[]) { +const getProfileFromRelays = async function (pubkey: string, relays: string[]): Promise { // get profile from the relays for pubkey linked to nip05 const filters: NostrFilter[] = [{ @@ -361,7 +361,9 @@ const getProfileFromRelays = async function (pubkey: string, relays: string[]) { if(!events || events.length === 0) { - throw new AppError(Err.SERVER_ERROR, 'Could not get profile event from the relays.', {relays}) + // do not log as error to save capacity + log.warn('Could not get profile event from the relays.', {relays}) + return undefined } const profile: NostrProfile = JSON.parse(events[events.length - 1].content) @@ -462,9 +464,6 @@ const deleteKeyPair = async function (): Promise { const getNormalizedRelayUrl = function (url: string): string { try { - if(!url.startsWith('wss://')) { - throw new Error('Relay needs to communicate over wss://') - } return utils.normalizeURL(url) } catch (e: any) { throw new AppError(Err.VALIDATION_ERROR, `Invalid relay URL: ${e.message}`) diff --git a/src/services/walletService.ts b/src/services/walletService.ts index 8869508f..4efa7491 100644 --- a/src/services/walletService.ts +++ b/src/services/walletService.ts @@ -412,7 +412,7 @@ const checkPendingReceived = async function () { const relays = getTagsByName(zapRequest.tags, 'relays') if(relays && relays.length > 0) { - const senderProfile = await NostrClient.getProfileFromRelays(sentFromPubkey, relays) + const senderProfile = await NostrClient.getProfileFromRelays(sentFromPubkey, relays) // returns undefined if not found if(senderProfile) { sentFrom = senderProfile.nip05 || senderProfile.name