Skip to content

Commit

Permalink
Merge pull request #1621 from kaloudis/info-modal
Browse files Browse the repository at this point in the history
Info Modal and Text components
  • Loading branch information
kaloudis committed Aug 25, 2023
2 parents 9aaae29 + 1c6cd8b commit cea0af7
Show file tree
Hide file tree
Showing 13 changed files with 629 additions and 77 deletions.
11 changes: 10 additions & 1 deletion App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import { Provider } from 'mobx-react';

import Stores from './stores/Stores';
import Navigation from './Navigation';
import NavigationService from './NavigationService';
import { AppContainer } from './components/layout/AppContainer';
import ExternalLinkModal from './components/Modals/ExternalLinkModal';
import AndroidNfcModal from './components/Modals/AndroidNfcModal';
import InfoModal from './components/Modals/InfoModal';
import { GestureHandlerRootView } from 'react-native-gesture-handler';

export default class App extends React.PureComponent {
Expand Down Expand Up @@ -35,9 +37,16 @@ export default class App extends React.PureComponent {
>
<AppContainer>
<GestureHandlerRootView style={{ flex: 1 }}>
<Navigation />
<Navigation
ref={(navigatorRef) => {
NavigationService.setTopLevelNavigator(
navigatorRef
);
}}
/>
<ExternalLinkModal />
<AndroidNfcModal />
<InfoModal />
</GestureHandlerRootView>
</AppContainer>
</Provider>
Expand Down
16 changes: 12 additions & 4 deletions Navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,10 @@ import EditFee from './views/EditFee';
// Embedded LND
import Seed from './views/Settings/Seed';
import Sync from './views/Sync';
import LspExplanation from './views/LspExplanation';
import LspExplanationRouting from './views/LspExplanationRouting';
import LspExplanationFees from './views/Explanations/LspExplanationFees';
import LspExplanationRouting from './views/Explanations/LspExplanationRouting';
import LspExplanationWrappedInvoices from './views/Explanations/LspExplanationWrappedInvoices';
import LspExplanationOverview from './views/Explanations/LspExplanationOverview';

const AppScenes = {
Wallet: {
Expand Down Expand Up @@ -290,12 +292,18 @@ const AppScenes = {
AddNotes: {
screen: AddNotes
},
LspExplanation: {
screen: LspExplanation
LspExplanationFees: {
screen: LspExplanationFees
},
LspExplanationRouting: {
screen: LspExplanationRouting
},
LspExplanationWrappedInvoices: {
screen: LspExplanationWrappedInvoices
},
LspExplanationOverview: {
screen: LspExplanationOverview
},
EmbeddedNodeSettings: {
screen: EmbeddedNode
},
Expand Down
23 changes: 23 additions & 0 deletions NavigationService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { NavigationActions } from 'react-navigation';

let _navigator;

function setTopLevelNavigator(navigatorRef) {
_navigator = navigatorRef;
}

function navigate(routeName: string, params?: any) {
_navigator.dispatch(
NavigationActions.navigate({
routeName,
params
})
);
}

// add other navigation functions that you need and export them

export default {
navigate,
setTopLevelNavigator
};
135 changes: 135 additions & 0 deletions components/Modals/InfoModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import React from 'react';
import { View, StyleSheet, Text } from 'react-native';
import { inject, observer } from 'mobx-react';

import NavigationService from '../../NavigationService';

import Button from '../Button';
import ModalBox from '../ModalBox';

import ModalStore from '../../stores/ModalStore';

import { localeString } from '../../utils/LocaleUtils';
import UrlUtils from '../../utils/UrlUtils';

interface InfoModalProps {
ModalStore: ModalStore;
}

@inject('ModalStore')
@observer
export default class InfoModal extends React.Component<InfoModalProps, {}> {
render() {
const { ModalStore } = this.props;
const {
showInfoModal,
infoModalText,
infoModalLink,
infoModalNav,
toggleInfoModal
} = ModalStore;

return (
<ModalBox
isOpen={showInfoModal}
style={{
backgroundColor: 'transparent',
minHeight: 200
}}
onClosed={() => toggleInfoModal()}
ref="modal"
>
<View
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}}
>
<View
style={{
backgroundColor: 'white',
borderRadius: 30,
padding: 30,
alignItems: 'center',
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2
}
}}
>
{typeof infoModalText === 'string' && (
<Text
style={{
fontFamily: 'Lato-Regular',
fontSize: 20,
marginBottom: 40
}}
>
{infoModalText}
</Text>
)}

{Array.isArray(infoModalText) &&
infoModalText.map((text: string, index: number) => (
<Text
key={index}
style={{
fontFamily: 'Lato-Regular',
fontSize: 20,
marginBottom: 40
}}
>
{text}
</Text>
))}

<View style={styles.buttons}>
{(infoModalLink || infoModalNav) && (
<View
style={{
...styles.button,
marginBottom: 25
}}
>
<Button
title={localeString(
'general.learnMore'
)}
onPress={() => {
toggleInfoModal();
if (infoModalLink)
UrlUtils.goToUrl(infoModalLink);
if (infoModalNav)
NavigationService.navigate(
infoModalNav
);
}}
tertiary
></Button>
</View>
)}
<View style={styles.button}>
<Button
title={localeString('general.close')}
onPress={() => toggleInfoModal()}
secondary
></Button>
</View>
</View>
</View>
</View>
</ModalBox>
);
}
}

const styles = StyleSheet.create({
buttons: {
width: '100%'
},
button: {
width: 350
}
});
64 changes: 64 additions & 0 deletions components/Text.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import * as React from 'react';
import { Text, TouchableOpacity } from 'react-native';
import { inject, observer } from 'mobx-react';
import { Row } from './layout/Row';

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

import ModalStore from '../stores/ModalStore';

interface TextProps {
ModalStore: ModalStore;
style?: any;
children: string;
infoText?: string | Array<string>;
infoLink?: string;
infoNav?: string;
}

@inject('ModalStore')
@observer
export default class ZeusText extends React.Component<TextProps, {}> {
render() {
const { children, style, infoText, infoLink, infoNav, ModalStore } =
this.props;
const { toggleInfoModal } = ModalStore;

const CoreText = () => (
<Row>
<Text
style={{
fontFamily: 'Lato-Regular',
color: themeColor('text'),
...style
}}
>
{children}
</Text>
{infoText && (
<Text
style={{
color: themeColor('text'),
...style,
fontWeight: 'bold'
}}
>
{' ⓘ'}
</Text>
)}
</Row>
);

if (infoText) {
return (
<TouchableOpacity
onPress={() => toggleInfoModal(infoText, infoLink, infoNav)}
>
<CoreText />
</TouchableOpacity>
);
}

return <CoreText />;
}
}
27 changes: 25 additions & 2 deletions locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@
"general.success": "Success",
"general.on": "On",
"general.off": "Off",
"general.close": "Close",
"general.learnMore": "Learn more",
"components.CollapsedQr.show": "Show QR",
"components.CollapsedQr.hide": "Hide QR",
"components.CollapsedQr.startNfc": "Start NFC broadcast",
Expand Down Expand Up @@ -479,7 +481,12 @@
"views.Receive.lspExplainer": "LSP will take the following amount as a setup fee:",
"views.Receive.lspExplainerRouting": "LSP will take the following amount as a fee:",
"views.Receive.goToLspSettings": "Go to LSP Settings",
"views.Receive.lspSwitchExplainer": "The LSP will wrap your invoices, making it easier to receive payments while protecting your privacy.",
"views.Receive.lspSwitchExplainer1": "The LSP will provide you with 0-conf channels that will allow you to send and receive payments on the Lightning network.",
"views.Receive.lspSwitchExplainer2": "The LSP will also wrap your invoices, making it easier to receive payments while protecting your privacy.",
"views.Receive.routeHintSwitchExplainer1": "Route hints provide information to find non-advertised, or unannounced, channels. This allows routing of payments to nodes that are not publicly visible on the network.",
"views.Receive.routeHintSwitchExplainer2": "It's helpful to toggle route hints on if you're using only unannounced channels, or if someone trying to pay you cannot reach you via your announced channels.",
"views.Receive.ampSwitchExplainer1": "Atomic Multi-path Payments (AMP) are a new type of Lightning payments that can be paid multiple times.",
"views.Receive.ampSwitchExplainer2": "Please note that AMP invoices are currently only compatible with LND nodes.",
"views.Receive.lspZeroAmt": "The LSP is incompatible with zero amounts. An unwrapped invoice has been generated. Your node's public key will be exposed.",
"views.Send.title": "Send",
"views.Send.lnPayment": "Lightning payment request",
Expand Down Expand Up @@ -787,6 +794,22 @@
"views.LspExplanation.text1": "Zeus is a self-custodial lightning wallet. In order to send or receive a lightning payment, you must open a lightning payment channel, which has a setup fee.",
"views.LspExplanation.text2": "Once the channel is set up, you'll only have to pay normal network fees until your channel exhausts its capacity.",
"views.LspExplanation.buttonText": "Learn more about liquidity",
"views.LspExplanation.buttonText2": "Learn more about how the LSP works",
"views.LspExplanationRouting.text1": "In order to provide you with some privacy and improve payment performance, the Lightning Service Provider (LSP) will do some heavy lifting for you with a technique called invoice wrapping. The fee is nominal; it's typically just a few satoshis.",
"views.LspExplanationRouting.text2": "You can turn off the LSP entirely and save on these fees by navigating to Settings > Lightning Service Provider (LSP), but you will sacrifice these privacy and payment performance gains."
"views.LspExplanationRouting.text2": "You can turn off the LSP entirely and save on these fees by navigating to Settings > Lightning Service Provider (LSP), but you will sacrifice these privacy and payment performance gains.",
"views.LspExplanationWrappedInvoices.title": "Wrapped invoices",
"views.LspExplanationWrappedInvoices.text1": "Wrapped invoices are invoices with extra layers around them. In this instance, the LSP will wrap an invoice generated from your node. The wrapped invoice will appear as if it is generated by the LSP.",
"views.LspExplanationWrappedInvoices.text2": "This has two benefits: 1) The end user doesn't have to expose their node's public key to senders. The LSP knows the receiver but no one else does. 2) Senders can use routes to the LSP to make a payment instead of having to find a direct path to the end user.",
"views.LspExplanationWrappedInvoices.text3": "Wrapped invoices uses the same preimage hash as the original invoice. This means that the LSP can't settle the payment without the final receiver completing the payment, so the LSP can't run away with the user's funds.",
"views.LspExplanationWrappedInvoices.text4": "Be sure to keep Zeus open when receiving payment as the LSP cannot settle payments on your behalf when you're offline.",
"views.LspExplanationOverview.title": "How does the LSP work?",
"views.LspExplanationOverview.text1": "The LSP uses wrapped invoices and 0-conf channels to instantly connect users to the Lightning network.",
"views.LspExplanationOverview.text2": "All you have to do is have the sender pay the invoice Zeus shows you, but here's what happens under the hood:",
"views.LspExplanationOverview.step1": "The receiver generates an invoices on their lightning node and then sends it to the LSP",
"views.LspExplanationOverview.step2": "The LSP creates a wrapped invoice and returns it to the receiver",
"views.LspExplanationOverview.step3": "The receiver gives the wrapped invoices to sender for them to pay",
"views.LspExplanationOverview.step4": "The sender pays the invoice, which goes through the LSP",
"views.LspExplanationOverview.step5": "The LSP detects the payment and opens up a 0-conf, just in time, channel to the receiver",
"views.LspExplanationOverview.step6": "The LSP then forwards the payment to the receiver using that new channel and the payment is completed",
"views.LspExplanationOverview.buttonText": "Learn more about wrapped invoices"
}
23 changes: 23 additions & 0 deletions stores/ModalStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ import { action, observable } from 'mobx';
export default class ModalStore {
@observable public showExternalLinkModal: boolean = false;
@observable public showAndroidNfcModal: boolean = false;
@observable public showInfoModal: boolean = false;
@observable public modalUrl: string;
@observable public clipboardValue: string;
@observable public infoModalText: string | Array<string> | undefined;
@observable public infoModalLink: string | undefined;
@observable public infoModalNav: string | undefined;
@observable public onPress: () => void;

/* External Link Modal */
Expand All @@ -13,6 +17,18 @@ export default class ModalStore {
this.showExternalLinkModal = status;
};

@action
public toggleInfoModal = (
text?: string | Array<string>,
link?: string,
nav?: string
) => {
this.showInfoModal = text ? true : false;
this.infoModalText = text;
this.infoModalLink = link;
this.infoModalNav = nav;
};

@action
public setUrl = (text: string) => {
this.modalUrl = text;
Expand Down Expand Up @@ -44,6 +60,13 @@ export default class ModalStore {
this.showAndroidNfcModal = false;
return true;
}
if (this.showInfoModal) {
this.showInfoModal = false;
this.infoModalText = '';
this.infoModalLink = '';
this.infoModalNav = '';
return true;
}
return false;
};
}
Loading

0 comments on commit cea0af7

Please sign in to comment.