-
-
Notifications
You must be signed in to change notification settings - Fork 123
/
Copy path[wallet].js
202 lines (186 loc) · 6.64 KB
/
[wallet].js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
import { getGetServerSideProps } from '@/api/ssrApollo'
import { Form, ClientInput, PasswordInput, CheckboxGroup, Checkbox } from '@/components/form'
import { CenterLayout } from '@/components/layout'
import { WalletSecurityBanner } from '@/components/banners'
import { WalletLogs } from '@/components/wallet-logger'
import { useToast } from '@/components/toast'
import { useRouter } from 'next/router'
import { useWallet } from '@/wallets/index'
import Info from '@/components/info'
import Text from '@/components/text'
import { autowithdrawInitial, AutowithdrawSettings } from '@/components/autowithdraw-shared'
import { canReceive, canSend, isConfigured } from '@/wallets/common'
import { SSR } from '@/lib/constants'
import WalletButtonBar from '@/components/wallet-buttonbar'
import { useWalletConfigurator } from '@/wallets/config'
import { Fragment, useCallback, useMemo } from 'react'
import { useMe } from '@/components/me'
import validateWallet from '@/wallets/validate'
import { ValidationError } from 'yup'
import { useFormikContext } from 'formik'
import { useWalletImage } from '@/components/wallet-image'
import styles from '@/styles/wallet.module.css'
export const getServerSideProps = getGetServerSideProps({ authRequired: true })
export default function WalletSettings () {
const toaster = useToast()
const router = useRouter()
const { wallet: name } = router.query
const wallet = useWallet(name)
const { me } = useMe()
const { save, detach } = useWalletConfigurator(wallet)
const image = useWalletImage(wallet)
const initial = useMemo(() => {
const initial = wallet?.def.fields.reduce((acc, field) => {
// We still need to run over all wallet fields via reduce
// even though we use wallet.config as the initial value
// since wallet.config is empty when wallet is not configured.
// Also, wallet.config includes general fields like
// 'enabled' and 'priority' which are not defined in wallet.fields.
return {
...acc,
[field.name]: wallet?.config?.[field.name] || field.defaultValue || ''
}
}, wallet?.config)
if (wallet?.def.fields.every(f => f.clientOnly)) {
return initial
}
return {
...initial,
...autowithdrawInitial({ me })
}
}, [wallet, me])
const validate = useCallback(async (data) => {
try {
await validateWallet(wallet.def, data,
{ yupOptions: { abortEarly: false }, topLevel: false, skipGenerated: true })
} catch (error) {
if (error instanceof ValidationError) {
return error.inner.reduce((acc, error) => {
acc[error.path] = error.message
return acc
}, {})
}
throw error
}
}, [wallet.def])
return (
<CenterLayout>
{image
? <img {...image} className={styles.walletBanner} />
: <h2 className='pb-2'>{wallet.def.card.title}</h2>}
<h6 className='text-muted text-center pb-3'><Text>{wallet.def.card.subtitle}</Text></h6>
<Form
initial={initial}
enableReinitialize
validate={validate}
onSubmit={async ({ amount, ...values }) => {
try {
const newConfig = !isConfigured(wallet)
// enable wallet if wallet was just configured
if (newConfig) {
values.enabled = true
}
await save(values, values.enabled)
toaster.success('saved settings')
router.push('/wallets')
} catch (err) {
console.error(err)
toaster.danger(err.message || err.toString?.())
}
}}
>
<SendWarningBanner walletDef={wallet.def} />
{wallet && <WalletFields wallet={wallet} />}
{wallet && <WalletComponents wallet={wallet} />}
<CheckboxGroup name='enabled'>
<Checkbox
disabled={!isConfigured(wallet)}
label='enabled'
name='enabled'
groupClassName='mb-0'
/>
</CheckboxGroup>
<ReceiveSettings walletDef={wallet.def} />
<WalletButtonBar
wallet={wallet} onDelete={async () => {
try {
await detach()
toaster.success('saved settings')
router.push('/wallets')
} catch (err) {
console.error(err)
const message = 'failed to detach: ' + err.message || err.toString?.()
toaster.danger(message)
}
}}
/>
</Form>
<div className='mt-3 w-100'>
{wallet && <WalletLogs wallet={wallet} embedded />}
</div>
</CenterLayout>
)
}
function SendWarningBanner ({ walletDef }) {
const { values } = useFormikContext()
if (!canSend({ def: walletDef, config: values }) || !walletDef.requiresConfig) return null
return <WalletSecurityBanner />
}
function ReceiveSettings ({ walletDef }) {
const { values } = useFormikContext()
return canReceive({ def: walletDef, config: values }) && <AutowithdrawSettings />
}
function WalletFields ({ wallet }) {
return wallet.def.fields
.map(({
name, label = '', type, help, optional, editable, requiredWithout,
validate, clientOnly, serverOnly, generated, ...props
}, i) => {
const rawProps = {
...props,
name,
initialValue: wallet.config?.[name],
readOnly: !SSR && isConfigured(wallet) && editable === false && !!wallet.config?.[name],
groupClassName: props.hidden ? 'd-none' : undefined,
label: label
? (
<div className='d-flex align-items-center'>
{label}
{/* help can be a string or object to customize the label */}
{help && (
<Info label={help.label}>
<Text>{help.text || help}</Text>
</Info>
)}
{optional && (
<small className='text-muted ms-2'>
{typeof optional === 'boolean' ? 'optional' : <Text>{optional}</Text>}
</small>
)}
</div>
)
: undefined,
required: !optional,
autoFocus: i === 0
}
if (type === 'text') {
return <ClientInput key={i} {...rawProps} />
}
if (type === 'password') {
return <PasswordInput key={i} {...rawProps} newPass />
}
return null
})
}
function WalletComponents ({ wallet }) {
if (!canSend({ def: wallet.def, config: wallet.config })) return null
return (
<div className='d-flex align-items-center justify-content-end column-gap-3'>
{
wallet.def.components?.map((Component, i) => {
return (<Fragment key={i}><Component wallet={wallet} /></Fragment>)
})
}
</div>
)
}