-
Notifications
You must be signed in to change notification settings - Fork 357
Fix: debounce fetch apps #1021
Fix: debounce fetch apps #1021
Changes from all commits
04776f4
7415444
cd1444e
da85abe
bc6be07
2f30977
2046e87
46cd3a7
28e5b26
6ba3f9d
fafd94f
41c3fdd
fa8b216
7313e78
285521e
cfe908d
bfef944
d679bf9
7c6c9cd
c80db53
f3edd9b
41330c5
8df4101
fff6c44
a2bf47a
9c4d4ae
cafafc8
280c100
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| import { Checkbox, Text } from '@gnosis.pm/safe-react-components' | ||
| import React from 'react' | ||
| import { useFormState } from 'react-final-form' | ||
| import styled from 'styled-components' | ||
|
|
||
| import { required } from 'src/components/forms/validator' | ||
| import Field from 'src/components/forms/Field' | ||
|
|
||
| const StyledCheckbox = styled(Checkbox)` | ||
| margin: 0; | ||
| ` | ||
|
|
||
| const AppAgreement = (): React.ReactElement => { | ||
| const { visited } = useFormState({ subscription: { visited: true } }) | ||
|
|
||
| // trick to prevent having the field validated by default. Not sure why this happens in this form | ||
mmv08 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| const validate = !visited.agreementAccepted ? undefined : required | ||
|
|
||
| return ( | ||
| <Field | ||
| component={StyledCheckbox} | ||
| label={ | ||
| <Text size="xl"> | ||
| This app is not a Gnosis product and I agree to use this app | ||
| <br /> | ||
| at my own risk. | ||
| </Text> | ||
| } | ||
| name="agreementAccepted" | ||
| type="checkbox" | ||
| validate={validate} | ||
| /> | ||
| ) | ||
| } | ||
|
|
||
| export default AppAgreement | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| import { TextField } from '@gnosis.pm/safe-react-components' | ||
| import createDecorator from 'final-form-calculate' | ||
| import React from 'react' | ||
| import { useField, useFormState } from 'react-final-form' | ||
|
|
||
| import { SafeApp } from 'src/routes/safe/components/Apps/types' | ||
| import { getAppInfoFromUrl, getIpfsLinkFromEns, uniqueApp } from 'src/routes/safe/components/Apps/utils' | ||
| import { composeValidators, required } from 'src/components/forms/validator' | ||
| import Field from 'src/components/forms/Field' | ||
| import { isValid as isURLValid } from 'src/utils/url' | ||
| import { isValidEnsName } from 'src/logic/wallets/ethAddresses' | ||
| import { useDebounce } from 'src/routes/safe/container/hooks/useDebounce' | ||
|
|
||
| const validateUrl = (url: string): string | undefined => (isURLValid(url) ? undefined : 'Invalid URL') | ||
|
|
||
| export const appUrlResolver = createDecorator({ | ||
| field: 'appUrl', | ||
| updates: { | ||
| appUrl: async (appUrl: string): Promise<string | undefined> => { | ||
| const ensContent = !isURLValid(appUrl) && isValidEnsName(appUrl) && (await getIpfsLinkFromEns(appUrl)) | ||
|
|
||
| if (ensContent) { | ||
| return ensContent | ||
| } | ||
|
|
||
| return appUrl | ||
| }, | ||
| }, | ||
| }) | ||
|
|
||
| export const AppInfoUpdater = ({ onAppInfo }: { onAppInfo: (appInfo: SafeApp) => void }): React.ReactElement => { | ||
| const { | ||
| input: { value: appUrl }, | ||
| } = useField('appUrl', { subscription: { value: true } }) | ||
| const debouncedValue = useDebounce(appUrl, 500) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wdyt about implementing this hook yourself? I added a link above where you can find the source code for it. |
||
|
|
||
| React.useEffect(() => { | ||
| const updateAppInfo = async () => { | ||
| const appInfo = await getAppInfoFromUrl(debouncedValue) | ||
| onAppInfo({ ...appInfo }) | ||
| } | ||
|
|
||
| if (isURLValid(debouncedValue)) { | ||
| updateAppInfo() | ||
| } | ||
| }, [debouncedValue, onAppInfo]) | ||
|
|
||
| return null | ||
| } | ||
|
|
||
| const AppUrl = ({ appList }: { appList: SafeApp[] }): React.ReactElement => { | ||
| const { visited } = useFormState({ subscription: { visited: true } }) | ||
|
|
||
| // trick to prevent having the field validated by default. Not sure why this happens in this form | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm.. so this is the same case as in AppAgreement component, I wonder what is so special about this form... |
||
| const validate = !visited.appUrl ? undefined : composeValidators(required, validateUrl, uniqueApp(appList)) | ||
|
|
||
| return ( | ||
| <Field label="App URL" name="appUrl" placeholder="App URL" type="text" component={TextField} validate={validate} /> | ||
| ) | ||
| } | ||
|
|
||
| export default AppUrl | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| import React from 'react' | ||
| import { useFormState } from 'react-final-form' | ||
|
|
||
| import { SafeApp } from 'src/routes/safe/components/Apps/types' | ||
| import { isAppManifestValid } from 'src/routes/safe/components/Apps/utils' | ||
|
|
||
| interface SubmitButtonStatusProps { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is there a reason to use an interface instead of a type?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not sure, I've been using interface lately. Do we have a decision made around that or a standard? https://github.com/typescript-cheatsheets/react-typescript-cheatsheet#function-components |
||
| appInfo: SafeApp | ||
| onSubmitButtonStatusChange: (disabled: boolean) => void | ||
| } | ||
|
|
||
| const SubmitButtonStatus = ({ appInfo, onSubmitButtonStatusChange }: SubmitButtonStatusProps): React.ReactElement => { | ||
| const { valid, validating, visited } = useFormState({ | ||
| subscription: { valid: true, validating: true, visited: true }, | ||
| }) | ||
|
|
||
| React.useEffect(() => { | ||
| // if non visited, fields were not evaluated yet. Then, the default value is considered invalid | ||
| const fieldsVisited = visited.agreementAccepted && visited.appUrl | ||
|
|
||
| onSubmitButtonStatusChange(validating || !valid || !fieldsVisited || !isAppManifestValid(appInfo)) | ||
| }, [validating, valid, visited, onSubmitButtonStatusChange, appInfo]) | ||
|
|
||
| return null | ||
| } | ||
|
|
||
| export default SubmitButtonStatus | ||
Uh oh!
There was an error while loading. Please reload this page.