Skip to content

Commit

Permalink
Merge pull request #1924 from shubhamkmr04/shubham/Contact_sharing
Browse files Browse the repository at this point in the history
Add functionality to share Contact via QR code
  • Loading branch information
kaloudis authored Jan 12, 2024
2 parents 4204bc5 + 9477b35 commit 3abb16d
Show file tree
Hide file tree
Showing 10 changed files with 371 additions and 55 deletions.
4 changes: 4 additions & 0 deletions Navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import NodeInfo from './views/NodeInfo';
import NetworkInfo from './views/NetworkInfo';
import Lockscreen from './views/Lockscreen';
import NostrContacts from './views/NostrContacts';
import ContactQR from './views/ContactQR';

// Settings views
import Settings from './views/Settings/Settings';
Expand Down Expand Up @@ -415,6 +416,9 @@ const AppScenes = {
},
NostrContacts: {
screen: NostrContacts
},
ContactQR: {
screen: ContactQR
}
};

Expand Down
6 changes: 6 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,12 @@
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="zeusln" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="zeuscontact" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
Expand Down
1 change: 1 addition & 0 deletions ios/zeus/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
<string>keyauth</string>
<string>lndconnect</string>
<string>zeusln</string>
<string>zeuscontact</string>
</array>
</dict>
</array>
Expand Down
7 changes: 2 additions & 5 deletions locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@
"general.content": "Content",
"general.lightningInvoice": "Lightning invoice",
"general.or": "or",
"general.readOnlyWallet": "Read-only wallet",
"general.reset": "Reset",
"general.other": "Other",
"general.reorder": "Reorder",
Expand Down Expand Up @@ -485,7 +484,6 @@
"views.OpenChannel.private": "Private",
"views.Payment.title": "Payment",
"views.Payment.inTransitPayment": "In Transit Payment",
"views.Payment.failedPayment": "Failed Payment",
"views.Payment.fee": "Fee",
"views.Payment.paymentHash": "Payment Hash",
"views.Payment.paymentPreimage": "Payment Preimage",
Expand Down Expand Up @@ -522,7 +520,6 @@
"views.PaymentRequest.lndGettingReadyReceive": "LND is getting ready to receive payments. Please wait.",
"views.PaymentRequest.isPmtHashSigValid": "Payment hash signature",
"views.PaymentRequest.isRelaysSigValid": "Relays signature",
"views.PaymentRequest.notAllowedToSend": "This wallet is not allowed to send funds!",
"views.Receive.title": "Receive",
"views.Receive.successCreate": "Successfully created invoice",
"views.Receive.warningLndHub": "Please note that LNDHub has a fixed on-chain address",
Expand Down Expand Up @@ -606,6 +603,8 @@
"views.NostrContacts.enterNpub": "Enter npub",
"views.NostrContacts.importContactsError": "Failed to import contacts. Please try again.",
"views.ContactDetails.saveToContacts": "Save to Contacts",
"views.ContactDetails.editAndSaveContact": "Edit and Save Contact",
"views.ContactInfo.title": "Contact Info",
"views.Settings.title": "Settings",
"views.Settings.enabled": "Enabled",
"views.Settings.disabled": "Disabled",
Expand Down Expand Up @@ -858,7 +857,6 @@
"views.ActivityFilter.onChainPayments": "On-chain payments",
"views.ActivityFilter.minimumAmount": "Minimum Amount (sats)",
"views.ActivityFilter.inTransit": "In transit payments",
"views.ActivityFilter.isFailed": "Failed payments",
"general.clearChanges": "Clear changes",
"views.Routing.RoutingEvent.sourceChannel": "Source Channel",
"views.Routing.RoutingEvent.destinationChannel": "Destination Channel",
Expand Down Expand Up @@ -954,7 +952,6 @@
"views.Settings.LightningAddress.nostrKeys.changeWarning": "Warning: changing your Nostr keys will delete your pending payments. Please redeem your pending payments before submitting new keys.",
"views.Settings.LightningAddress.explainer1": "To get started with a lightning address you must first have a lightning channel and inbound liquidity.",
"views.Settings.LightningAddress.explainer2": "The easiest way to do so is to purchase a 0-conf channel from our LSP, OLYMPUS by ZEUS. Just generate an invoice and pay yourself from another lightning wallet.",
"views.Settings.LightningAddress.explainer3": "For best results, open up a channel with our node, OLYMPUS by ZEUS.",
"views.Settings.LightningAddress.get0ConfChan": "Get 0-conf channel",
"views.Settings.LightningAddressSettings.title": "Lightning address settings",
"views.Settings.LightningAddressSettings.automaticallyAccept": "Automatically accept payments on startup",
Expand Down
13 changes: 13 additions & 0 deletions utils/handleAnything.ts
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,19 @@ const handleAnything = async (
localeString('utils.handleAnything.invalidLnurlParams')
);
});
} else if (data.startsWith('zeuscontact:')) {
const zeusContactData = data.replace('zeuscontact:', '');
const contact = JSON.parse(zeusContactData);

if (contact?.contactId) {
return [
'ContactDetails',
{
nostrContact: contact,
isNostrContact: true
}
];
}
} else {
if (isClipboardValue) return false;
throw new Error(localeString('utils.handleAnything.notValid'));
Expand Down
147 changes: 117 additions & 30 deletions views/ContactDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ import Screen from '../components/Screen';
import Button from '../components/Button';
import LoadingIndicator from '../components/LoadingIndicator';
import Header from '../components/Header';
import { Row } from '../components/layout/Row';

import LightningBolt from '../assets/images/SVG/Lightning Bolt.svg';
import BitcoinIcon from '../assets/images/SVG/BitcoinIcon.svg';
import KeySecurity from '../assets/images/SVG/Key Security.svg';
import VerifiedAccount from '../assets/images/SVG/Verified Account.svg';
import EditContact from '../assets/images/SVG/Pen.svg';
import Star from '../assets/images/SVG/Star.svg';
import QR from '../assets/images/SVG/QR.svg';

import { themeColor } from '../utils/ThemeUtils';
import LinkingUtils from '../utils/LinkingUtils';
Expand All @@ -29,16 +31,17 @@ interface ContactDetailsProps {
}

interface ContactItem {
lnAddress: string;
onchainAddress: string;
pubkey: string;
nip05: string;
nostrNpub: string;
id: string;
lnAddress: string[];
onchainAddress: string[];
pubkey: string[];
nip05: string[];
nostrNpub: string[];
name: string;
description: string;
photo: string | null;
isFavourite: boolean;
id: string;
contactId: string;
banner: string;
}
interface ContactDetailsState {
Expand All @@ -55,17 +58,18 @@ export default class ContactDetails extends React.Component<

this.state = {
contact: {
lnAddress: '',
onchainAddress: '',
pubkey: '',
nip05: '',
nostrNpub: '',
lnAddress: [''],
onchainAddress: [''],
pubkey: [''],
nip05: [''],
nostrNpub: [''],
name: '',
description: '',
photo: null,
isFavourite: false,
id: '',
banner: ''
contactId: '',
banner: '',
id: ''
},
isLoading: true,
isNostrContact: false
Expand Down Expand Up @@ -105,7 +109,9 @@ export default class ContactDetails extends React.Component<
if (contactsString && contactId) {
const existingContact = JSON.parse(contactsString);
const contact = existingContact.find(
(contact: ContactItem) => contact.id === contactId
(contact: ContactItem) =>
contact.contactId === contactId ||
contact.id === contactId
);

// Store the found contact in the component's state
Expand Down Expand Up @@ -141,7 +147,7 @@ export default class ContactDetails extends React.Component<

// Find the index of the contact with the same name
const contactIndex = existingContacts.findIndex(
(contact) => contact.id === updatedContact.id
(contact) => contact.contactId === updatedContact.contactId
);

if (contactIndex !== -1) {
Expand Down Expand Up @@ -206,19 +212,23 @@ export default class ContactDetails extends React.Component<
};

render() {
const { contact, isLoading } = this.state;
const { contact, isLoading, isNostrContact } = this.state;
const { navigation } = this.props;

const nostrContact = this.props.navigation.getParam(
'nostrContact',
null
);
const StarButton = () => (
<TouchableOpacity onPress={this.toggleFavorite}>
<Star
fill={contact.isFavourite ? themeColor('text') : 'none'}
stroke={contact.isFavourite ? 'none' : themeColor('text')}
strokeWidth={2}
style={{ alignSelf: 'center' }}
style={{ alignSelf: 'center', marginRight: 16 }}
/>
</TouchableOpacity>
);

const EditContactButton = () => (
<TouchableOpacity
onPress={() =>
Expand All @@ -234,6 +244,65 @@ export default class ContactDetails extends React.Component<
/>
</TouchableOpacity>
);

// Function to add prefixes to addresses based on their types
const addPrefixToAddresses = (
addresses: string[] | undefined,
prefix: string
) =>
(addresses || [])
.filter(Boolean)
.map((address) => `${prefix}${address}`);

const QRButton = () => {
const { contact } = this.state;
const { lnAddress, onchainAddress, pubkey, nostrNpub, nip05 } =
contact;
return (
<TouchableOpacity
onPress={() => {
const contactDataWithoutPhoto = {
...this.state.contact
};

// Check if 'photo' exists and doesn't start with 'http'
if (
contactDataWithoutPhoto.photo &&
!contactDataWithoutPhoto.photo.startsWith('http')
) {
delete contactDataWithoutPhoto.photo;
}

// Add the 'zeuscontact:' prefix to the contactData parameter
const zeusContactData = `zeuscontact:${JSON.stringify(
contactDataWithoutPhoto
)}`;
navigation.navigate('ContactQR', {
contactData: zeusContactData,
addressData: [
...addPrefixToAddresses(
lnAddress,
'lightning:'
),
...addPrefixToAddresses(pubkey, 'lightning:'),
...addPrefixToAddresses(
onchainAddress,
'bitcoin:'
),
...addPrefixToAddresses(nostrNpub, 'nostr:'),
...addPrefixToAddresses(nip05, 'nostr:')
]
});
}}
>
<QR
fill={themeColor('text')}
style={{ alignSelf: 'center' }}
/>
</TouchableOpacity>
);
};

return (
<>
{isLoading ? (
Expand All @@ -254,12 +323,13 @@ export default class ContactDetails extends React.Component<
<Header
leftComponent="Back"
centerComponent={
!this.state.isNostrContact && (
<EditContactButton />
)
!isNostrContact && <EditContactButton />
}
rightComponent={
!this.state.isNostrContact && <StarButton />
<Row>
<StarButton />
<QRButton />
</Row>
}
centerContainerStyle={{
paddingRight: 6,
Expand Down Expand Up @@ -555,14 +625,31 @@ export default class ContactDetails extends React.Component<
</View>
)}
</ScrollView>
{this.state.isNostrContact && (
<Button
onPress={() => this.importToContacts()}
title={localeString(
'views.ContactDetails.saveToContacts'
)}
containerStyle={{ paddingBottom: 12 }}
/>
{isNostrContact && (
<>
<Button
onPress={() => this.importToContacts()}
title={localeString(
'views.ContactDetails.saveToContacts'
)}
containerStyle={{ paddingBottom: 12 }}
/>
<Button
onPress={() => {
navigation.goBack();
navigation.navigate('AddContact', {
prefillContact: nostrContact,
isEdit: true,
isNostrContact
});
}}
title={localeString(
'views.ContactDetails.editAndSaveContact'
)}
containerStyle={{ paddingBottom: 12 }}
secondary
/>
</>
)}
</Screen>
)}
Expand Down
Loading

0 comments on commit 3abb16d

Please sign in to comment.