From d794fc030f56aff487f9bd8cddee3c60a77cb5ef Mon Sep 17 00:00:00 2001 From: Evan Kaloudis Date: Sat, 23 Mar 2024 09:39:46 -0400 Subject: [PATCH 1/2] Send: lookup Nostr contacts --- locales/en.json | 2 + utils/handleAnything.test.ts | 2 + utils/handleAnything.ts | 86 +++++++++++++++++++++++++++++++++++- views/ContactDetails.tsx | 18 ++++++-- 4 files changed, 103 insertions(+), 5 deletions(-) diff --git a/locales/en.json b/locales/en.json index 54d838606..d851b255a 100644 --- a/locales/en.json +++ b/locales/en.json @@ -918,6 +918,8 @@ "utils.handleAnything.lnurlAuthNotSupported": "LnurlAuth not supported by your node implementation", "utils.handleAnything.unsupportedLnurlType": "Unsupported lnurl type", "utils.handleAnything.invalidLnurlParams": "Could not parse lnurl params", + "utils.handleAnything.nostrProfileError": "Error fetching Nostr profile", + "utils.handleAnything.addressError": "Error fetching address", "stores.InvoicesStore.errorCreatingInvoice": "Error creating invoice", "stores.InvoicesStore.errorGeneratingAddress": "Error generating new address", "stores.InvoicesStore.zeroAmountLndhub": "LNDHub instance might not support zero-amount invoices", diff --git a/utils/handleAnything.test.ts b/utils/handleAnything.test.ts index 9dc5b4675..756ffe995 100644 --- a/utils/handleAnything.test.ts +++ b/utils/handleAnything.test.ts @@ -13,6 +13,8 @@ jest.mock('../stores/Stores', () => ({ invoicesStore: { getPayReq: jest.fn() } })); jest.mock('react-native-blob-util', () => ({})); +jest.mock('react-native-encrypted-storage', () => ({})); +jest.mock('react-native-fs', () => ({})); jest.mock('js-lnurl', () => ({ getParams: () => mockGetLnurlParams })); diff --git a/utils/handleAnything.ts b/utils/handleAnything.ts index 89400d6b4..3f6bc3274 100644 --- a/utils/handleAnything.ts +++ b/utils/handleAnything.ts @@ -10,11 +10,83 @@ import NodeUriUtils from './NodeUriUtils'; import { localeString } from './LocaleUtils'; import BackendUtils from './BackendUtils'; +// Nostr +import { DEFAULT_NOSTR_RELAYS } from '../stores/SettingsStore'; +import { relayInit, nip05, nip19 } from 'nostr-tools'; +import ContactUtils from './ContactUtils'; + const { nodeInfoStore, invoicesStore, unitsStore, settingsStore } = stores; const isClipboardValue = (data: string) => handleAnything(data, undefined, true); +const attemptNip05Lookup = async (data: string) => { + try { + const lookup: any = await nip05.queryProfile(data); + const pubkey = lookup.pubkey; + return await nostrProfileLookup(pubkey); + } catch (e) { + throw new Error(localeString('utils.handleAnything.addressError')); + } +}; + +const nostrProfileLookup = async (data: string) => { + let profile: any; + + const pubkey = data; + const profilesEventsPromises = DEFAULT_NOSTR_RELAYS.map( + async (relayItem) => { + const relay = relayInit(relayItem); + relay.on('connect', () => { + console.log(`connected to ${relay.url}`); + }); + relay.on('error', () => { + console.log(`failed to connect to ${relay.url}`); + }); + + await relay.connect(); + return relay.list([ + { + authors: [pubkey], + kinds: [0] + } + ]); + } + ); + + await Promise.all(profilesEventsPromises).then((profilesEventsArrays) => { + const profileEvents = profilesEventsArrays + .flat() + .filter((event) => event !== undefined); + + profileEvents.forEach((item: any) => { + try { + const content = JSON.parse(item.content); + if (!profile || item.created_at > profile.timestamp) { + profile = { + content, + timestamp: item.created_at + }; + } + } catch (error: any) { + throw new Error( + `Error parsing JSON for item with ID ${item.id}: ${error.message}` + ); + } + }); + }); + + return [ + 'ContactDetails', + { + nostrContact: await ContactUtils.transformContactData( + profile.content + ), + isNostrContact: true + } + ]; +}; + const handleAnything = async ( data: string, setAmount?: string, @@ -259,8 +331,8 @@ const handleAnything = async ( throw new Error(error); } }) - .catch(() => { - throw new Error(error); + .catch(async () => { + return await attemptNip05Lookup(data); }); } } else if (value.includes('config=') && value.includes('lnd.config')) { @@ -394,6 +466,16 @@ const handleAnything = async ( localeString('utils.handleAnything.invalidLnurlParams') ); }); + } else if (AddressUtils.isValidNpub(data)) { + try { + const decoded = nip19.decode(data); + const pubkey = decoded.data.toString(); + return await nostrProfileLookup(pubkey); + } catch (e) { + throw new Error( + localeString('utils.handleAnything.nostrProfileError') + ); + } } else if (data.startsWith('zeuscontact:')) { const zeusContactData = data.replace('zeuscontact:', ''); const contact = JSON.parse(zeusContactData); diff --git a/views/ContactDetails.tsx b/views/ContactDetails.tsx index 7f937a88d..bd80aa6f9 100644 --- a/views/ContactDetails.tsx +++ b/views/ContactDetails.tsx @@ -93,6 +93,10 @@ export default class ContactDetails extends React.Component< const contactsString = await EncryptedStorage.getItem( 'zeus-contacts' ); + const isNostrContact = this.props.navigation.getParam( + 'isNostrContact', + null + ); if (contactsString && contactId) { const existingContact = JSON.parse(contactsString); @@ -103,9 +107,17 @@ export default class ContactDetails extends React.Component< ); // Store the found contact in the component's state - this.setState({ contact, isLoading: false }); + this.setState({ + contact, + isNostrContact, + isLoading: false + }); } else { - this.setState({ contact: nostrContact, isLoading: false }); + this.setState({ + contact: nostrContact, + isNostrContact, + isLoading: false + }); } } catch (error) { console.log('Error fetching contact:', error); @@ -315,7 +327,7 @@ export default class ContactDetails extends React.Component< } rightComponent={ - + {!isNostrContact && } } From 766f50f0f07f634883b875b4e218939cb03d0482 Mon Sep 17 00:00:00 2001 From: shubham Date: Thu, 9 May 2024 22:50:08 +0530 Subject: [PATCH 2/2] handleAnything: Add try catch block while connecting to relay --- utils/handleAnything.ts | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/utils/handleAnything.ts b/utils/handleAnything.ts index 3f6bc3274..f60896e03 100644 --- a/utils/handleAnything.ts +++ b/utils/handleAnything.ts @@ -36,21 +36,23 @@ const nostrProfileLookup = async (data: string) => { const pubkey = data; const profilesEventsPromises = DEFAULT_NOSTR_RELAYS.map( async (relayItem) => { - const relay = relayInit(relayItem); - relay.on('connect', () => { - console.log(`connected to ${relay.url}`); - }); - relay.on('error', () => { - console.log(`failed to connect to ${relay.url}`); - }); + try { + const relay = relayInit(relayItem); + relay.on('connect', () => { + console.log(`connected to ${relay.url}`); + }); + relay.on('error', (): any => { + console.log(`failed to connect to ${relay.url}`); + }); - await relay.connect(); - return relay.list([ - { - authors: [pubkey], - kinds: [0] - } - ]); + await relay.connect(); + return relay.list([ + { + authors: [pubkey], + kinds: [0] + } + ]); + } catch (e) {} } );