diff --git a/Navigation.ts b/Navigation.ts index f60eac283..e9802bd9e 100644 --- a/Navigation.ts +++ b/Navigation.ts @@ -48,6 +48,7 @@ import Sponsors from './views/Settings/Sponsors'; import Olympians from './views/Settings/Olympians'; import Gods from './views/Settings/Gods'; import Mortals from './views/Settings/Mortals'; +import PaymentsSettings from './views/Settings/PaymentsSettings'; // Routing import Routing from './views/Routing/Routing'; @@ -239,6 +240,9 @@ const AppScenes = { }, SparkQRScanner: { screen: SparkQRScanner + }, + PaymentsSettings: { + screen: PaymentsSettings } }; diff --git a/locales/en.json b/locales/en.json index cfc29899f..fb49ce159 100644 --- a/locales/en.json +++ b/locales/en.json @@ -426,7 +426,10 @@ "views.Settings.Display.defaultView": "Default view", "views.Settings.Display.displayNickname": "Display node nickname on main views", "views.Settings.privacy": "Privacy", + "views.Settings.payments": "Payments", "views.Settings.Privacy.title": "Privacy settings", + "views.Settings.Payments.title": "Payments settings", + "views.Settings.Payments.defaultFeeLimit": "Default Fee Limit", "views.Settings.Privacy.blockExplorer": "Default Block explorer", "views.Settings.Privacy.customBlockExplorer": "Custom Block explorer", "views.Settings.Privacy.lurkerMode": "Lurker mode", diff --git a/stores/SettingsStore.ts b/stores/SettingsStore.ts index 6fee75f18..302be945f 100644 --- a/stores/SettingsStore.ts +++ b/stores/SettingsStore.ts @@ -39,6 +39,12 @@ interface DisplaySettings { displayNickname?: boolean; } +interface PaymentSettings { + defaultFeeMethod?: string; + defaultFeePercentage?: string; + defaultFeeFixed?: string; +} + interface Settings { nodes?: Array; selectedNode?: number; @@ -53,6 +59,7 @@ interface Settings { locale?: string; privacy: PrivacySettings; display: DisplaySettings; + payments: PaymentSettings; } export const BLOCK_EXPLORER_KEYS = [ @@ -185,6 +192,11 @@ export default class SettingsStore { defaultView: 'Keypad', displayNickname: false }, + payments: { + defaultFeeMethod: 'fixed', + defaultFeePercentage: '0.5', + defaultFeeFixed: '100' + }, scramblePin: true, loginBackground: false, fiat: DEFAULT_FIAT diff --git a/views/PaymentRequest.tsx b/views/PaymentRequest.tsx index 2adbc8a12..cfd6a5f26 100644 --- a/views/PaymentRequest.tsx +++ b/views/PaymentRequest.tsx @@ -74,12 +74,24 @@ export default class PaymentRequest extends React.Component< maxParts: '16', maxShardAmt: '', feeLimitSat: '100', - feeOption: 'sats', + feeOption: 'fixed', maxFeePercent: '0.5', outgoingChanId: null, lastHopPubkey: null }; + async UNSAFE_componentWillMount() { + const { SettingsStore } = this.props; + const { getSettings } = SettingsStore; + const settings = await getSettings(); + + this.setState({ + feeOption: settings.payments.defaultFeeMethod || 'fixed', + feeLimitSat: settings.payments.defaultFeeFixed || '100', + maxFeePercent: settings.payments.defaultFeePercentage || '0.5' + }); + } + subscribePayment = (streamingCall: string) => { const { handlePayment, handlePaymentError } = this.props.TransactionsStore; @@ -148,7 +160,7 @@ export default class PaymentRequest extends React.Component< this.props; let feeLimitSat = fee_limit_sat; - if (feeOption == 'sats') { + if (feeOption == 'fixed') { // If the fee limit is not set, use a default routing fee calculation if (!fee_limit_sat) { const { pay_req } = InvoicesStore; @@ -469,7 +481,7 @@ export default class PaymentRequest extends React.Component< style={{ width: '50%', opacity: - feeOption == 'sats' + feeOption == 'fixed' ? 1 : 0.25 }} @@ -482,7 +494,7 @@ export default class PaymentRequest extends React.Component< } onPressIn={() => this.setState({ - feeOption: 'sats' + feeOption: 'fixed' }) } /> @@ -493,7 +505,7 @@ export default class PaymentRequest extends React.Component< top: 28, right: 30, opacity: - feeOption == 'sats' + feeOption == 'fixed' ? 1 : 0.25 }} diff --git a/views/Settings/PaymentsSettings.tsx b/views/Settings/PaymentsSettings.tsx new file mode 100644 index 000000000..f65191f69 --- /dev/null +++ b/views/Settings/PaymentsSettings.tsx @@ -0,0 +1,213 @@ +import * as React from 'react'; +import { Text, View } from 'react-native'; +import { Header, Icon } from 'react-native-elements'; +import { inject, observer } from 'mobx-react'; +import SettingsStore from '../../stores/SettingsStore'; +import { localeString } from '../../utils/LocaleUtils'; +import { themeColor } from '../../utils/ThemeUtils'; + +import LoadingIndicator from '../../components/LoadingIndicator'; +import TextInput from '../../components/TextInput'; + +interface PaymentsSettingsProps { + navigation: any; + SettingsStore: SettingsStore; +} + +interface PaymentsSettingsState { + feeLimitMethod: string; + feeLimit: string; + feePercentage: string; +} + +@inject('SettingsStore') +@observer +export default class PaymentsSettings extends React.Component< + PaymentsSettingsProps, + PaymentsSettingsState +> { + state = { + feeLimitMethod: 'fixed', + feeLimit: '100', + feePercentage: '0.5' + }; + + async UNSAFE_componentWillMount() { + const { SettingsStore } = this.props; + const { getSettings } = SettingsStore; + const settings = await getSettings(); + + this.setState({ + feeLimitMethod: settings.payments.defaultFeeMethod || 'fixed', + feeLimit: settings.payments.defaultFeeFixed || '100', + feePercentage: settings.payments.defaultFeePercentage || '0.5' + }); + } + + handleSave = async () => { + const { feeLimitMethod, feeLimit, feePercentage } = this.state; + const { SettingsStore } = this.props; + const { updateSettings } = SettingsStore; + await updateSettings({ + payments: { + defaultFeeMethod: feeLimitMethod, + defaultFeePercentage: feePercentage, + defaultFeeFixed: feeLimit + } + }); + }; + + renderSeparator = () => ( + + ); + + render() { + const { navigation, SettingsStore } = this.props; + const { feeLimit, feeLimitMethod, feePercentage } = this.state; + const { loading }: any = SettingsStore; + + const BackButton = () => ( + { + this.handleSave(); + navigation.navigate('Settings', { + refresh: true + }); + }} + color={themeColor('text')} + underlayColor="transparent" + /> + ); + + return ( + +
} + centerComponent={{ + text: localeString('views.Settings.Payments.title'), + style: { + color: themeColor('text'), + fontFamily: 'Lato-Regular' + } + }} + backgroundColor={themeColor('background')} + containerStyle={{ + borderBottomWidth: 0 + }} + /> + {loading ? ( + + ) : ( + + + {`${localeString( + 'views.Settings.Payments.defaultFeeLimit' + )}`} + + + + + this.setState({ + feeLimit: text + }) + } + onPressIn={() => + this.setState({ + feeLimitMethod: 'fixed' + }) + } + /> + + {`${localeString('general.sats')}`} + + + this.setState({ + feePercentage: text + }) + } + onPressIn={() => + this.setState({ + feeLimitMethod: 'percent' + }) + } + /> + + {'%'} + + + + )} + + ); + } +} diff --git a/views/Settings/Settings.tsx b/views/Settings/Settings.tsx index 665665485..07f654199 100644 --- a/views/Settings/Settings.tsx +++ b/views/Settings/Settings.tsx @@ -14,6 +14,7 @@ import BrushIcon from '../../assets/images/SVG/Brush.svg'; import LanguageIcon from '../../assets/images/SVG/Globe.svg'; import HelpIcon from '../../assets/images/SVG/Help Icon.svg'; import NodeOn from '../../assets/images/SVG/Node On.svg'; +import SendIcon from '../../assets/images/SVG/Send'; import NodeIdenticon, { NodeTitle } from './../../components/NodeIdenticon'; import { themeColor } from './../../utils/ThemeUtils'; @@ -242,6 +243,42 @@ export default class Settings extends React.Component { )} + + {selectedNode && ( + + + navigation.navigate('PaymentsSettings') + } + > + + + + + {localeString('views.Settings.payments')} + + + + + + + )} + {selectedNode && BackendUtils.supportsMessageSigning() ? ( { height: 138, borderRadius: 10, alignSelf: 'center', - top: 30 + top: 45 }} > { height: 90, borderRadius: 10, alignSelf: 'center', - top: 50 + top: 60 }} > { height: 45, borderRadius: 10, alignSelf: 'center', - top: 70 + top: 75 }} >