This repository has been archived by the owner on Nov 10, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 362
/
index.tsx
174 lines (160 loc) · 5.82 KB
/
index.tsx
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
import InputAdornment from '@material-ui/core/InputAdornment'
import { makeStyles } from '@material-ui/core/styles'
import CheckCircle from '@material-ui/icons/CheckCircle'
import React, { ReactElement, ReactNode } from 'react'
import { FormApi } from 'final-form'
import { useParams } from 'react-router'
import { useSelector } from 'react-redux'
import { ScanQRWrapper } from 'src/components/ScanQRModal/ScanQRWrapper'
import OpenPaper from 'src/components/Stepper/OpenPaper'
import { StepperPageFormProps } from 'src/components/Stepper'
import AddressInput from 'src/components/forms/AddressInput'
import Field from 'src/components/forms/Field'
import TextField from 'src/components/forms/TextField'
import {
mustBeEthereumAddress,
noErrorsOn,
required,
composeValidators,
validAddressBookName,
} from 'src/components/forms/validator'
import Block from 'src/components/layout/Block'
import Col from 'src/components/layout/Col'
import Paragraph from 'src/components/layout/Paragraph'
import { FIELD_LOAD_ADDRESS, FIELD_LOAD_NAME } from 'src/routes/load/components/fields'
import { secondary } from 'src/theme/variables'
import { getSafeInfo } from 'src/logic/safe/utils/safeInformation'
import { addressBookName } from 'src/logic/addressBook/store/selectors'
const useStyles = makeStyles({
root: {
display: 'flex',
maxWidth: '460px',
marginTop: '12px',
},
check: {
color: '#03AE60',
height: '20px',
},
links: {
'&>a': {
color: secondary,
},
},
})
export const SAFE_ADDRESS_NOT_VALID = 'Address given is not a valid Safe address'
// In case of an error here, it will be swallowed by final-form
// So if you're experiencing any strang behaviours like freeze or hanging
// Don't mind to check if everything is OK inside this function :)
export const safeFieldsValidation = async (values): Promise<Record<string, string>> => {
const errors = {}
const address = values[FIELD_LOAD_ADDRESS]
if (!address || mustBeEthereumAddress(address) !== undefined) {
return errors
}
// If getSafeInfo does not provide data, it's not a valid safe.
const safeInfo = await getSafeInfo(address).catch(() => null)
if (!safeInfo) {
errors[FIELD_LOAD_ADDRESS] = SAFE_ADDRESS_NOT_VALID
}
return errors
}
interface DetailsFormProps {
errors: Record<string, string>
form: FormApi
}
const DetailsForm = ({ errors, form }: DetailsFormProps): ReactElement => {
const classes = useStyles()
const { safeAddress } = useParams<{ safeAddress?: string }>()
const safeName = useSelector((state) => (safeAddress ? addressBookName(state, { address: safeAddress }) : ''))
const handleScan = (value: string, closeQrModal: () => void): void => {
form.mutators.setValue(FIELD_LOAD_ADDRESS, value)
closeQrModal()
}
return (
<>
<Block margin="md">
<Paragraph color="primary" noMargin size="md">
You are about to add an existing Gnosis Safe. First, choose a name and enter the Safe address. The name is
only stored locally and will never be shared with Gnosis or any third parties.
<br />
Your connected wallet does not have to be the owner of this Safe. In this case, the interface will provide you
a read-only view.
</Paragraph>
<Paragraph color="primary" size="md" className={classes.links}>
Don't have the address of the Safe you created?{' '}
<a
href="https://help.gnosis-safe.io/en/articles/4971293-i-don-t-remember-my-safe-address-where-can-i-find-it"
rel="noopener noreferrer"
target="_blank"
>
This article explains how to find it.
</a>
</Paragraph>
</Block>
<Block className={classes.root}>
<Col xs={11}>
<Field
defaultValue={safeName}
component={TextField}
name={FIELD_LOAD_NAME}
placeholder="Name of the Safe*"
text="Safe name"
type="text"
validate={composeValidators(required, validAddressBookName)}
testId="load-safe-name-field"
/>
</Col>
</Block>
<Block className={classes.root} margin="lg">
<Col xs={11}>
<AddressInput
defaultValue={safeAddress}
fieldMutator={(val) => {
form.mutators.setValue(FIELD_LOAD_ADDRESS, val)
}}
// eslint-disable-next-line
// @ts-ignore
inputAdornment={
noErrorsOn(FIELD_LOAD_ADDRESS, errors) && {
endAdornment: (
<InputAdornment position="end">
<CheckCircle className={classes.check} data-testid="valid-address" />
</InputAdornment>
),
}
}
name={FIELD_LOAD_ADDRESS}
placeholder="Safe Address*"
text="Safe Address"
testId="load-safe-address-field"
/>
</Col>
<Col center="xs" className={classes} middle="xs" xs={1}>
<ScanQRWrapper handleScan={handleScan} />
</Col>
</Block>
<Block margin="sm">
<Paragraph className={classes.links} color="primary" noMargin size="md">
By continuing you consent to the{' '}
<a href="https://gnosis-safe.io/terms" rel="noopener noreferrer" target="_blank">
terms of use
</a>{' '}
and{' '}
<a href="https://gnosis-safe.io/privacy" rel="noopener noreferrer" target="_blank">
privacy policy
</a>
.
</Paragraph>
</Block>
</>
)
}
const DetailsPage = () =>
function LoadSafeDetails(controls: ReactNode, { errors, form }: StepperPageFormProps): ReactElement {
return (
<OpenPaper controls={controls}>
<DetailsForm errors={errors} form={form} />
</OpenPaper>
)
}
export default DetailsPage