Skip to content

Commit e959790

Browse files
committed
Remove settings from flow
1 parent ecedd80 commit e959790

File tree

6 files changed

+267
-26
lines changed

6 files changed

+267
-26
lines changed

components/multi-step-form.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,17 @@ export function useStep () {
203203
return steps[stepIndex]
204204
}
205205

206+
export function useIsFirstStep () {
207+
const stepIndex = useStepIndex()
208+
return stepIndex === 0
209+
}
210+
211+
export function useIsLastStep () {
212+
const maxSteps = useMaxSteps()
213+
const stepIndex = useStepIndex()
214+
return stepIndex === maxSteps - 1
215+
}
216+
206217
export function useNext () {
207218
const { next } = useContext(MultiStepFormContext)
208219
return next

pages/wallets/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ export default function Wallet () {
115115
<WalletLayoutSubHeader>use real bitcoin</WalletLayoutSubHeader>
116116
<div className='text-center'>
117117
<WalletLayoutLink href='/wallets/logs'>wallet logs</WalletLayoutLink>
118+
<span className='mx-2'></span>
119+
<WalletLayoutLink href='/wallets/settings'>settings</WalletLayoutLink>
118120
{showPassphrase && (
119121
<>
120122
<span className='mx-2'></span>

pages/wallets/settings.js

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
import { useCallback } from 'react'
2+
import InputGroup from 'react-bootstrap/InputGroup'
3+
import { useField } from 'formik'
4+
import { useRouter } from 'next/router'
5+
import { useMutation, useQuery } from '@apollo/client'
6+
import { Checkbox, Form, Input, SubmitButton } from '@/components/form'
7+
import Info from '@/components/info'
8+
import { useToast } from '@/components/toast'
9+
import AccordianItem from '@/components/accordian-item'
10+
import { isNumber } from '@/lib/format'
11+
import { walletSettingsSchema } from '@/lib/validate'
12+
import { SET_WALLET_SETTINGS, WALLET_SETTINGS } from '@/wallets/client/fragments'
13+
import { WalletLayout, WalletLayoutHeader } from '@/wallets/client/components'
14+
15+
export default function WalletSettings () {
16+
const { data } = useQuery(WALLET_SETTINGS)
17+
const [setSettings] = useMutation(SET_WALLET_SETTINGS)
18+
const toaster = useToast()
19+
const router = useRouter()
20+
21+
const onSubmit = useCallback(async (settings) => {
22+
try {
23+
await setSettings({
24+
variables: { settings },
25+
update: (cache, { data }) => {
26+
cache.writeQuery({
27+
query: WALLET_SETTINGS,
28+
data: {
29+
walletSettings: {
30+
__typename: 'WalletSettings',
31+
...data?.setWalletSettings
32+
}
33+
}
34+
})
35+
}
36+
})
37+
router.push('/wallets')
38+
} catch (err) {
39+
console.error(err)
40+
toaster.danger('failed to save wallet')
41+
}
42+
}, [setSettings, toaster, router])
43+
44+
const initial = {
45+
receiveCreditsBelowSats: data?.walletSettings?.receiveCreditsBelowSats ?? 10,
46+
sendCreditsBelowSats: data?.walletSettings?.sendCreditsBelowSats ?? 10,
47+
autoWithdrawThreshold: data?.walletSettings?.autoWithdrawThreshold ?? 10000,
48+
autoWithdrawMaxFeePercent: data?.walletSettings?.autoWithdrawMaxFeePercent ?? 1,
49+
autoWithdrawMaxFeeTotal: data?.walletSettings?.autoWithdrawMaxFeeTotal ?? 1,
50+
proxyReceive: data?.walletSettings?.proxyReceive ?? true
51+
}
52+
53+
return (
54+
<WalletLayout>
55+
<Form
56+
enableReinitialize
57+
initial={initial}
58+
schema={walletSettingsSchema}
59+
onSubmit={onSubmit}
60+
>
61+
<div className='py-5'>
62+
<WalletLayoutHeader>wallet settings</WalletLayoutHeader>
63+
</div>
64+
<Settings />
65+
<div className='d-flex mt-5 justify-content-end align-items-center'>
66+
<SubmitButton variant='primary'>save</SubmitButton>
67+
</div>
68+
</Form>
69+
</WalletLayout>
70+
)
71+
}
72+
73+
function Settings () {
74+
return (
75+
<>
76+
<AutowithdrawSettings />
77+
<AccordianItem
78+
header='advanced'
79+
body={
80+
<>
81+
<LightningAddressSettings />
82+
<CowboyCreditsSettings />
83+
</>
84+
}
85+
/>
86+
</>
87+
)
88+
}
89+
90+
function AutowithdrawSettings () {
91+
const [{ value: threshold }] = useField('autoWithdrawThreshold')
92+
const sendThreshold = Math.max(Math.floor(threshold / 10), 1)
93+
94+
return (
95+
<>
96+
<Input
97+
label='desired balance'
98+
name='autoWithdrawThreshold'
99+
hint={isNumber(sendThreshold) ? `will attempt autowithdrawal when your balance exceeds ${sendThreshold * 11} sats` : undefined}
100+
append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
101+
required
102+
type='number'
103+
min={0}
104+
groupClassName='mb-2'
105+
/>
106+
<Input
107+
label={
108+
<div className='d-flex align-items-center'>
109+
max fee rate
110+
<Info>
111+
<ul>
112+
<li>configure fee budget for autowithdrawals</li>
113+
<li>if max fee total is higher for a withdrawal, we will use it instead to find a route</li>
114+
<li>higher fee settings increase the likelihood of successful withdrawals</li>
115+
</ul>
116+
</Info>
117+
</div>
118+
}
119+
name='autoWithdrawMaxFeePercent'
120+
append={<InputGroup.Text>%</InputGroup.Text>}
121+
required
122+
type='number'
123+
min={0}
124+
/>
125+
<Input
126+
label={
127+
<div className='d-flex align-items-center'>
128+
max fee total
129+
<Info>
130+
<ul>
131+
<li>configure fee budget for autowithdrawals</li>
132+
<li>if max fee rate is higher for a withdrawal, we will use it instead to find a route to your wallet</li>
133+
<li>higher fee settings increase the likelihood of successful withdrawals</li>
134+
</ul>
135+
</Info>
136+
</div>
137+
}
138+
name='autoWithdrawMaxFeeTotal'
139+
append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
140+
required
141+
type='number'
142+
min={0}
143+
/>
144+
</>
145+
)
146+
}
147+
148+
function LightningAddressSettings () {
149+
return (
150+
<>
151+
<Checkbox
152+
label={
153+
<div className='d-flex align-items-center'>enhance privacy of my lightning address
154+
<Info>
155+
<ul>
156+
<li>Enabling this setting hides details (ie node pubkey) of your attached wallets when anyone pays your SN lightning address or lnurl-pay</li>
157+
<li>The lightning invoice will appear to have SN's node as the destination to preserve your wallet's privacy</li>
158+
<li className='fw-bold'>This will incur in a 10% fee</li>
159+
<li>Disable this setting to receive payments directly to your attached wallets (which will reveal their details to the payer)</li>
160+
<li>Note: this privacy behavior is standard for internal zaps/payments on SN, and this setting only applies to external payments</li>
161+
</ul>
162+
</Info>
163+
</div>
164+
}
165+
name='proxyReceive'
166+
groupClassName='mb-3'
167+
/>
168+
</>
169+
)
170+
}
171+
172+
function CowboyCreditsSettings () {
173+
return (
174+
<>
175+
<Input
176+
label={
177+
<div className='d-flex align-items-center'>
178+
receive credits for zaps below
179+
<Info>
180+
<ul>
181+
<li>we will not attempt to forward zaps below this amount to you, you will receive credits instead</li>
182+
<li>this setting is useful if small amounts are expensive to receive for you</li>
183+
</ul>
184+
</Info>
185+
</div>
186+
}
187+
name='receiveCreditsBelowSats'
188+
required
189+
append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
190+
type='number'
191+
min={0}
192+
/>
193+
<Input
194+
label={
195+
<div className='d-flex align-items-center'>
196+
send credits for zaps below
197+
<Info>
198+
<ul>
199+
<li>we will not attempt to send zaps below this amount from your wallet if you have enough credits</li>
200+
<li>this setting is useful if small amounts are expensive to send for you</li>
201+
</ul>
202+
</Info>
203+
</div>
204+
}
205+
name='sendCreditsBelowSats'
206+
required
207+
append={<InputGroup.Text className='text-monospace'>sats</InputGroup.Text>}
208+
type='number'
209+
min={0}
210+
/>
211+
</>
212+
)
213+
}

wallets/client/components/form/hooks.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { isTemplate, isWallet, protocolClientSchema, protocolFields, protocolFormId, walletLud16Domain } from '@/wallets/lib/util'
22
import { createContext, useContext, useEffect, useMemo, useCallback, useState } from 'react'
3+
import { useRouter } from 'next/router'
34
import { useWalletProtocolUpsert } from '@/wallets/client/hooks'
45
import { MultiStepForm, useFormState, useStep } from '@/components/multi-step-form'
56
import { parseNwcUrl } from '@/wallets/lib/validate'
@@ -133,6 +134,7 @@ export function useSaveWallet () {
133134
const wallet = useWallet()
134135
const [formState] = useFormState()
135136
const upsert = useWalletProtocolUpsert()
137+
const router = useRouter()
136138

137139
const save = useCallback(async () => {
138140
let walletId = isWallet(wallet) ? wallet.id : undefined
@@ -147,7 +149,8 @@ export function useSaveWallet () {
147149
)
148150
walletId ??= id
149151
}
150-
}, [wallet, formState, upsert])
152+
router.push('/wallets')
153+
}, [wallet, formState, upsert, router])
151154

152155
return save
153156
}

wallets/client/components/form/index.js

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,14 @@ import { Checkbox, Form, Input, PasswordInput, SubmitButton } from '@/components
77
import CancelButton from '@/components/cancel-button'
88
import Text from '@/components/text'
99
import Info from '@/components/info'
10-
import { useFormState, useMaxSteps, useNext, useStepIndex } from '@/components/multi-step-form'
11-
import { isTemplate, isWallet, protocolDisplayName, protocolFormId, protocolLogName, walletLud16Domain } from '@/wallets/lib/util'
10+
import { useIsFirstStep, useIsLastStep, useNext } from '@/components/multi-step-form'
11+
import { isTemplate, protocolDisplayName, protocolFormId, protocolLogName, walletLud16Domain } from '@/wallets/lib/util'
1212
import { WalletGuide, WalletLayout, WalletLayoutHeader, WalletLayoutImageOrName, WalletLogs } from '@/wallets/client/components'
1313
import { TemplateLogsProvider, useTestSendPayment, useWalletLogger, useTestCreateInvoice, useWalletSupport } from '@/wallets/client/hooks'
1414
import ArrowRight from '@/svgs/arrow-right-s-fill.svg'
1515
import { useFormikContext } from 'formik'
1616

17-
import { WalletMultiStepFormContextProvider, Step, useWallet, useWalletProtocols, useProtocol, useProtocolForm } from './hooks'
18-
import { Settings } from './settings'
17+
import { WalletMultiStepFormContextProvider, Step, useWallet, useWalletProtocols, useProtocol, useProtocolForm, useSaveWallet } from './hooks'
1918
import { BackButton, SkipButton } from './button'
2019

2120
export function WalletMultiStepForm ({ wallet }) {
@@ -33,8 +32,7 @@ export function WalletMultiStepForm ({ wallet }) {
3332
const steps = useMemo(() =>
3433
[
3534
support.send && Step.SEND,
36-
support.receive && Step.RECEIVE,
37-
Step.SETTINGS
35+
support.receive && Step.RECEIVE
3836
].filter(Boolean),
3937
[support])
4038

@@ -51,7 +49,7 @@ export function WalletMultiStepForm ({ wallet }) {
5149
// and can thus render a different form for send vs. receive
5250
if (step === Step.SEND) return <WalletForm key={step} />
5351
if (step === Step.RECEIVE) return <WalletForm key={step} />
54-
return <Settings key={step} />
52+
return null
5553
})}
5654
</WalletMultiStepFormContextProvider>
5755
</div>
@@ -92,12 +90,21 @@ function WalletProtocolSelector () {
9290
function WalletProtocolForm () {
9391
const wallet = useWallet()
9492
const [protocol] = useProtocol()
95-
const next = useNext()
93+
94+
// on the last step, we save the wallet, otherwise we just go to the next step
95+
const isLastStep = useIsLastStep()
96+
const formNext = useNext()
97+
const formSave = useSaveWallet()
98+
9699
const testSendPayment = useTestSendPayment(protocol)
97100
const testCreateInvoice = useTestCreateInvoice(protocol)
98101
const logger = useWalletLogger(protocol)
99102
const [{ fields, initial, schema }, setFormState] = useProtocolForm(protocol)
100103

104+
const next = useCallback(() => {
105+
isLastStep ? formSave() : formNext()
106+
}, [isLastStep, formSave, formNext])
107+
101108
// create a copy of values to avoid mutating the original
102109
const onSubmit = useCallback(async ({ ...values }) => {
103110
const lud16Domain = walletLud16Domain(wallet.name)
@@ -152,25 +159,31 @@ function WalletProtocolForm () {
152159
}
153160

154161
function WalletProtocolFormNavigator () {
155-
const wallet = useWallet()
156-
const stepIndex = useStepIndex()
157-
const maxSteps = useMaxSteps()
158-
const [formState] = useFormState()
159-
160-
// was something already configured or was something configured just now?
161-
const configExists = (isWallet(wallet) && wallet.protocols.length > 0) || Object.keys(formState).length > 0
162-
163-
// don't allow going to settings as last step with nothing configured
164-
const hideSkip = stepIndex === maxSteps - 2 && !configExists
162+
// show 'cancel' in the first step
163+
const showCancel = useIsFirstStep()
164+
// show 'save' instead of 'next' in the last step
165+
const isLastStep = useIsLastStep()
166+
// show 'skip' if there's a next step
167+
const showSkip = !isLastStep
165168

166169
return (
167170
<div className='d-flex justify-content-end align-items-center'>
168-
{stepIndex === 0 ? <CancelButton>cancel</CancelButton> : <BackButton />}
169-
{!hideSkip ? <SkipButton /> : <div className='ms-auto' />}
170-
<SubmitButton variant='primary' className='ps-3 pe-2 d-flex align-items-center'>
171-
next
172-
<ArrowRight width={24} height={24} />
173-
</SubmitButton>
171+
{showCancel ? <CancelButton>cancel</CancelButton> : <BackButton />}
172+
{showSkip ? <SkipButton /> : <div className='ms-auto' />}
173+
{
174+
isLastStep
175+
? (
176+
<SubmitButton variant='primary' className='d-flex align-items-center'>
177+
save
178+
</SubmitButton>
179+
)
180+
: (
181+
<SubmitButton variant='primary' className='ps-3 pe-2 d-flex align-items-center'>
182+
next
183+
<ArrowRight width={24} height={24} />
184+
</SubmitButton>
185+
)
186+
}
174187
</div>
175188
)
176189
}

wallets/client/components/form/settings.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ export function Settings () {
4545
})
4646
}
4747
})
48-
router.push('/wallets')
4948
} catch (err) {
5049
console.error(err)
5150
toaster.danger('failed to save wallet')

0 commit comments

Comments
 (0)