Skip to content

Commit a7f766b

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

File tree

6 files changed

+321
-26
lines changed

6 files changed

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

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
}

0 commit comments

Comments
 (0)