Skip to content
This repository has been archived by the owner on Nov 10, 2023. It is now read-only.

Fix: debounce fetch apps #1021

Merged
merged 28 commits into from
Jul 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
04776f4
Fix: debounce fetch apps
nicosampler Jun 16, 2020
7415444
Merge branch 'development' into issue-999
fernandomg Jul 22, 2020
cd1444e
Merge branch 'development' into issue-999
fernandomg Jul 22, 2020
da85abe
refactor: fix AddAppForm name and add missing types
fernandomg Jul 23, 2020
bc6be07
add `use-lodash-debounce` hook to test debounce functionality
fernandomg Jul 23, 2020
2f30977
refactor AddAppForm to use the observable pattern
fernandomg Jul 23, 2020
2046e87
memoize `getAppInfoFromUrl` to prevent requesting the same informatio…
fernandomg Jul 23, 2020
46cd3a7
Merge branch 'development' into issue-999
fernandomg Jul 23, 2020
28e5b26
prevent requesting data if url is not valid
fernandomg Jul 23, 2020
6ba3f9d
remove logging
fernandomg Jul 23, 2020
fafd94f
prevent validating form before visiting the fields
fernandomg Jul 23, 2020
41c3fdd
refactor AddAppForm
fernandomg Jul 23, 2020
fa8b216
Merge branch 'development' into issue-999
fernandomg Jul 23, 2020
7313e78
fix: change `any` to `unknown`
fernandomg Jul 23, 2020
285521e
fix: `uitls.ts` types and imports
fernandomg Jul 23, 2020
cfe908d
Merge branch 'development' into issue-999
mmv08 Jul 24, 2020
bfef944
refactor: rename `isSubmitDisabled` to `onSubmitButtonStatusChange` prop
fernandomg Jul 24, 2020
d679bf9
refactor: rename `agreement` to `agreementAccepted`
fernandomg Jul 24, 2020
7c6c9cd
refactor: reimplement `useDebounce` hook in-app
fernandomg Jul 24, 2020
c80db53
refactor: extract app manifest verification to a helper function
fernandomg Jul 24, 2020
f3edd9b
Merge branch 'development' into issue-999
fernandomg Jul 24, 2020
41330c5
fix: prevent accessing `contentWindow` if `iframe` is `null`
fernandomg Jul 24, 2020
8df4101
Merge branch 'fix/contentWindow-of-null' into issue-999
fernandomg Jul 24, 2020
fff6c44
Merge branch 'development' into issue-999
fernandomg Jul 24, 2020
a2bf47a
Merge branch 'development' into issue-999
fernandomg Jul 27, 2020
9c4d4ae
Merge branch 'development' into issue-999
fernandomg Jul 29, 2020
cafafc8
fix: `getAppInfoFromOrigin` return type
fernandomg Jul 29, 2020
280c100
Merge branch 'development' into issue-999
mmv08 Jul 30, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@
"immortal-db": "^1.0.3",
"immutable": "^4.0.0-rc.12",
"js-cookie": "^2.2.1",
"lodash.debounce": "^4.0.8",
mmv08 marked this conversation as resolved.
Show resolved Hide resolved
"lodash.memoize": "^4.1.2",
"material-ui-search-bar": "^1.0.0-beta.13",
"notistack": "https://github.com/gnosis/notistack.git#v0.9.4",
Expand Down
205 changes: 0 additions & 205 deletions src/routes/safe/components/Apps/AddAppForm.tsx

This file was deleted.

36 changes: 36 additions & 0 deletions src/routes/safe/components/Apps/AddAppForm/AppAgreement.tsx
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
62 changes: 62 additions & 0 deletions src/routes/safe/components/Apps/AddAppForm/AppUrl.tsx
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)
Copy link
Member

Choose a reason for hiding this comment

The 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
Copy link
Member

Choose a reason for hiding this comment

The 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
27 changes: 27 additions & 0 deletions src/routes/safe/components/Apps/AddAppForm/SubmitButtonStatus.tsx
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 {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a reason to use an interface instead of a type?
As far I understand we should use type unless it could be extended in somewhere else.

Copy link
Contributor

Choose a reason for hiding this comment

The 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
Loading