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

AddNotes: Add notes after payment #1447

Merged
merged 11 commits into from
May 30, 2023
1 change: 1 addition & 0 deletions App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export default class App extends React.PureComponent {
ActivityStore={Stores.activityStore}
PosStore={Stores.posStore}
ModalStore={Stores.modalStore}
NotesStore={Stores.NotesStore}
>
<AppContainer>
<Navigation />
Expand Down
4 changes: 4 additions & 0 deletions Navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import ImportAccount from './views/Accounts/ImportAccount';
import ImportAccountQRScanner from './views/Accounts/ImportAccountQRScanner';
import BumpFee from './views/BumpFee';
import QR from './views/QR';
import AddNotes from './views/AddNotes';

// POS
import Order from './views/Order';
Expand Down Expand Up @@ -258,6 +259,9 @@ const AppScenes = {
},
QR: {
screen: QR
},
AddNotes: {
screen: AddNotes
}
};

Expand Down
3 changes: 3 additions & 0 deletions locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@
"views.Payment.creationDate": "Creation Date",
"views.Payment.path": "Path",
"views.Payment.paths": "Paths",
"views.Payment.notes": "Notes",
"views.PaymentRequest.title": "Lightning Invoice",
"views.PaymentRequest.error": "Error loading invoice",
"views.PaymentRequest.customAmt": "Custom Amount",
Expand Down Expand Up @@ -444,6 +445,8 @@
"views.SendingLightning.sending": "Sending Transaction",
"views.SendingLightning.success": "Transaction successfully sent",
"views.SendingLightning.paymentHash": "Payment Hash",
"views.SendingLightning.AddANote": "Add a note",
"views.SendingLightning.UpdateNote": "Update note",
"views.SendingLightning.goToWallet": "Go to Wallet",
"views.SendingLightning.lowFeeLimitMessage": "This payment may have failed due to a low fee limit. Try again with a higher fee limit",
"views.SendingLightning.tryAgain": "Try Again",
Expand Down
31 changes: 31 additions & 0 deletions stores/NotesStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { action, observable } from 'mobx';
import EncryptedStorage from 'react-native-encrypted-storage';

export default class NotesStore {
@observable public noteKeys: string[] = [];

@action
public storeNoteKeys = async (key: string, notes: string) => {
if (!this.noteKeys.includes(key)) {
if (notes) {
this.noteKeys.push(key);
}
try {
await EncryptedStorage.setItem(
'note-Keys',
JSON.stringify(this.noteKeys)
);
} catch (error) {
console.error('Error saving to encrypted storage');
}
}
};

@action
public removeNoteKeys = (key: string) => {
const index = this.noteKeys.indexOf(key);
if (index !== -1) {
this.noteKeys.splice(index, 1);
}
};
}
3 changes: 3 additions & 0 deletions stores/Stores.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import MessageSignStore from './MessageSignStore';
import ActivityStore from './ActivityStore';
import PosStore from './PosStore';
import ModalStore from './ModalStore';
import NotesStore from './NotesStore';

class Stores {
public channelsStore: ChannelsStore;
Expand All @@ -32,6 +33,7 @@ class Stores {
public activityStore: ActivityStore;
public posStore: PosStore;
public modalStore: ModalStore;
public NotesStore: NotesStore;

constructor() {
this.settingsStore = new SettingsStore();
Expand All @@ -55,6 +57,7 @@ class Stores {
this.feeStore = new FeeStore(this.settingsStore, this.nodeInfoStore);
this.utxosStore = new UTXOsStore(this.settingsStore);
this.messageSignStore = new MessageSignStore();
this.NotesStore = new NotesStore();
this.activityStore = new ActivityStore(
this.settingsStore,
this.paymentsStore,
Expand Down
120 changes: 120 additions & 0 deletions views/AddNotes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import * as React from 'react';
import { View, TextInput } from 'react-native';
import EncryptedStorage from 'react-native-encrypted-storage';
import { inject, observer } from 'mobx-react';

import Header from '../components/Header';
import Screen from '../components/Screen';
import Button from '../components/Button';

import { localeString } from '../utils/LocaleUtils';
import { themeColor } from '../utils/ThemeUtils';

import NotesStore from '../stores/NotesStore';

interface AddNotesProps {
navigation: any;
NotesStore: NotesStore;
}
interface AddNotesState {
notes?: string;
payment_hash?: string;
txid?: string;
RPreimage?: string;
}

@inject('NotesStore')
@observer
export default class AddNotes extends React.Component<
AddNotesProps,
AddNotesState
> {
constructor(props: any) {
super(props);
const payment_hash: string = this.props.navigation.getParam(
'payment_hash',
null
);
const txid: string = this.props.navigation.getParam('txid', null);
const RPreimage: string = this.props.navigation.getParam(
'getRPreimage',
null
);

this.state = {
notes: '',
payment_hash,
txid,
RPreimage
};
}
async componentDidMount() {
const key: string =
'note-' +
(this.state.txid ||
this.state.payment_hash ||
this.state.RPreimage);
const storedNotes = await EncryptedStorage.getItem(key);
if (storedNotes) {
this.setState({ notes: storedNotes });
}
}

render() {
const { navigation, NotesStore } = this.props;
const { storingNoteKeys, removeNoteKeys } = NotesStore;
const { payment_hash, txid, RPreimage } = this.state;
const { notes } = this.state;
return (
<Screen>
<Header
leftComponent="Back"
centerComponent={{
text: notes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this text shouldn't be dependent on whether the note in the state is set, rather if a previous note has been saved

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same with the button at the bottom of this view

? localeString('views.SendingLightning.UpdateNote')
: localeString('views.SendingLightning.AddANote'),
style: {
color: themeColor('text'),
fontFamily: 'Lato-Regular',
fontSize: 20
}
}}
navigation={navigation}
/>
<View style={{ padding: 20 }}>
<TextInput
onChangeText={(text: string) => {
this.setState({ notes: text });
if (!text) {
const key: string =
'note-' +
(payment_hash || txid || RPreimage);
removeNoteKeys(key);
}
}}
multiline
numberOfLines={0}
style={{ fontSize: 20, color: themeColor('text') }}
value={notes}
/>
</View>
<Button
title={
notes
? localeString('views.SendingLightning.UpdateNote')
: localeString('views.SendingLightning.AddANote')
}
onPress={async () => {
const key: string =
'note-' + (payment_hash || txid || RPreimage);
await EncryptedStorage.setItem(key, notes);
await storingNoteKeys(key, notes);
navigation.goBack();
}}
containerStyle={{ position: 'absolute', bottom: 40 }}
buttonStyle={{ padding: 15 }}
/>
</Screen>
);
}
}
96 changes: 86 additions & 10 deletions views/Invoice.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,47 @@
import * as React from 'react';
import { StyleSheet, ScrollView, View } from 'react-native';
import EncryptedStorage from 'react-native-encrypted-storage';

import { StyleSheet, ScrollView, View, TouchableOpacity } from 'react-native';
import { Icon } from 'react-native-elements';

import Amount from '../components/Amount';
import Header from '../components/Header';
import KeyValue from '../components/KeyValue';
import Screen from '../components/Screen';
import Button from '../components/Button';
import { Row } from '../components/layout/Row';

import Invoice from '../models/Invoice';

import { localeString } from '../utils/LocaleUtils';
import { themeColor } from '../utils/ThemeUtils';

import EditNotes from '../assets/images/SVG/Pen.svg';

interface InvoiceProps {
navigation: any;
}

export default class InvoiceView extends React.Component<InvoiceProps> {
state = {
storedNotes: ''
};
async componentDidMount() {
const { navigation } = this.props;
const invoice: Invoice = navigation.getParam('invoice', null);
navigation.addListener('didFocus', () => {
EncryptedStorage.getItem('note-' + invoice.getRPreimage)
.then((storedNotes) => {
this.setState({ storedNotes });
})
.catch((error) => {
console.error('Error retrieving notes:', error);
});
});
}
render() {
const { navigation } = this.props;
const { storedNotes } = this.state;
const invoice: Invoice = navigation.getParam('invoice', null);
const {
fallback_addr,
Expand All @@ -37,14 +60,26 @@ export default class InvoiceView extends React.Component<InvoiceProps> {
const privateInvoice = invoice.private;

const QRButton = () => (
<Icon
name="qr-code"
onPress={() => {
navigation.navigate('QR', { value: getPaymentRequest });
}}
color={themeColor('text')}
underlayColor="transparent"
/>
<View style={{ marginTop: -12 }}>
<Icon
name="qr-code"
onPress={() => {
navigation.navigate('QR', { value: getPaymentRequest });
}}
color={themeColor('text')}
underlayColor="transparent"
/>
</View>
);
const EditNotesButton = () => (
<TouchableOpacity
onPress={() =>
navigation.navigate('AddNotes', { getRPreimage })
}
style={{ marginTop: -12, alignSelf: 'center', marginRight: 6 }}
>
<EditNotes height={40} width={40} />
</TouchableOpacity>
);

return (
Expand All @@ -58,7 +93,12 @@ export default class InvoiceView extends React.Component<InvoiceProps> {
fontFamily: 'Lato-Regular'
}
}}
rightComponent={!!getPaymentRequest && <QRButton />}
rightComponent={
<Row>
<EditNotesButton />
{!!getPaymentRequest && <QRButton />}
</Row>
}
navigation={navigation}
/>
<ScrollView>
Expand Down Expand Up @@ -163,6 +203,42 @@ export default class InvoiceView extends React.Component<InvoiceProps> {
sensitive
/>
)}
{storedNotes && (
<TouchableOpacity
onPress={() =>
navigation.navigate('AddNotes', {
getRPreimage
})
}
>
<KeyValue
keyValue={localeString(
'views.Payment.notes'
)}
value={storedNotes}
sensitive
/>
</TouchableOpacity>
)}
{getRPreimage && (
<Button
title={
storedNotes
? localeString(
'views.SendingLightning.UpdateNote'
)
: localeString(
'views.SendingLightning.AddANote'
)
}
onPress={() =>
navigation.navigate('AddNotes', {
getRPreimage
})
}
containerStyle={{ marginTop: 15 }}
/>
)}

{!!getDescriptionHash && (
<KeyValue
Expand Down