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

Feature: support redirect param on creation page, redirect to the Safe app page if Safe was created from the landing #3790

Merged
merged 5 commits into from
Apr 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 10 additions & 0 deletions src/logic/hooks/useQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { useMemo } from 'react'
import { useLocation } from 'react-router-dom'

const useQuery = (): URLSearchParams => {
const { search } = useLocation()

return useMemo(() => new URLSearchParams(search), [search])
}

export { useQuery }
25 changes: 24 additions & 1 deletion src/routes/CreateSafePage/components/SafeCreationProcess.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ import { trackEvent } from 'src/utils/googleTagManager'
import { CREATE_SAFE_EVENTS } from 'src/utils/events/createLoadSafe'
import Track from 'src/components/Track'
import { didTxRevert } from 'src/logic/safe/store/actions/transactions/utils/transactionHelpers'
import { useQuery } from 'src/logic/hooks/useQuery'
import { ADDRESSED_ROUTE } from 'src/routes/routes'

export const InlinePrefixedEthHashInfo = styled(PrefixedEthHashInfo)`
display: inline-flex;
Expand Down Expand Up @@ -186,6 +188,8 @@ function SafeCreationProcess(): ReactElement {
const dispatch = useDispatch()
const userAddress = useSelector(userAccountSelector)
const chainId = useSelector(currentChainId)
const query = useQuery()
const redirect = query.get('redirect')

const [showModal, setShowModal] = useState(false)
const [modalData, setModalData] = useState<ModalDataType>({ safeAddress: '' })
Expand Down Expand Up @@ -269,10 +273,29 @@ function SafeCreationProcess(): ReactElement {
goToWelcomePage()
}

function onClickModalButton() {
const onClickModalButton = () => {
removeFromStorage(SAFE_PENDING_CREATION_STORAGE_KEY)

const { safeName, safeCreationTxHash, safeAddress } = modalData

if (redirect) {
// If the URL includes ADDRESSED_ROUTE template, then we need to replace it with the new safe address
if (redirect.includes(ADDRESSED_ROUTE)) {
history.push({
pathname: generateSafeRoute(redirect, {
shortName: getShortName(),
safeAddress,
}),
})
} else {
history.push({
pathname: redirect,
})
}

return
}

history.push({
pathname: generateSafeRoute(SAFE_ROUTES.ASSETS_BALANCES, {
shortName: getShortName(),
Expand Down
11 changes: 6 additions & 5 deletions src/routes/SafeAppLandingPage/SafeAppLandingPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as safeAppsGatewaySDK from '@gnosis.pm/safe-react-gateway-sdk'

import SafeAppLandingPage from './SafeAppLandingPage'
import { render, screen, waitFor, waitForElementToBeRemoved } from 'src/utils/test-utils'
import { history, OPEN_SAFE_ROUTE, WELCOME_ROUTE } from 'src/routes/routes'
import { history, SAFE_ROUTES, OPEN_SAFE_ROUTE, WELCOME_ROUTE } from 'src/routes/routes'
import * as appUtils from 'src/routes/safe/components/Apps/utils'
import { FETCH_STATUS } from 'src/utils/requests'
import { saveToStorage } from 'src/utils/storage'
Expand Down Expand Up @@ -131,7 +131,11 @@ describe('<SafeAppLandingPage>', () => {
// when the Loader is removed we show the create new safe button
await waitForElementToBeRemoved(() => screen.getByRole('progressbar'))
const createNewSafeLinkNode = screen.getByText('Create new Safe').closest('a')
expect(createNewSafeLinkNode).toHaveAttribute('href', OPEN_SAFE_ROUTE)
const openSafeRouteWithRedirect = `${OPEN_SAFE_ROUTE}?redirect=${encodeURIComponent(
`${SAFE_ROUTES.APPS}?appUrl=${SAFE_APP_URL_FROM_MANIFEST}`,
)}`

expect(createNewSafeLinkNode).toHaveAttribute('href', openSafeRouteWithRedirect)
})

it('Redirects to the Demo Safe App', async () => {
Expand Down Expand Up @@ -159,9 +163,6 @@ describe('<SafeAppLandingPage>', () => {

render(<SafeAppLandingPage />)

const loaderNode = screen.getByRole('progressbar')
expect(loaderNode).toBeInTheDocument()

await waitFor(() => {
expect(window.location.pathname).toBe(WELCOME_ROUTE)
})
Expand Down
39 changes: 15 additions & 24 deletions src/routes/SafeAppLandingPage/SafeAppLandingPage.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,33 @@
import { ReactElement, useCallback, useEffect } from 'react'
import { useLocation } from 'react-router-dom'
import { ReactElement, useCallback, useEffect, useMemo } from 'react'
import { Redirect } from 'react-router-dom'
import styled from 'styled-components'
import { Card, Loader } from '@gnosis.pm/safe-react-components'

import { isValidChainId } from 'src/config'
import { history, WELCOME_ROUTE } from 'src/routes/routes'
import { WELCOME_ROUTE } from 'src/routes/routes'
import { useAppList } from 'src/routes/safe/components/Apps/hooks/appList/useAppList'
import { SafeApp } from 'src/routes/safe/components/Apps/types'
import { getAppInfoFromUrl } from 'src/routes/safe/components/Apps/utils'
import { setChainId } from 'src/logic/config/utils'
import { useQuery } from 'src/logic/hooks/useQuery'
import useAsync from 'src/logic/hooks/useAsync'
import SafeAppDetails from 'src/routes/SafeAppLandingPage/components/SafeAppsDetails'
import TryDemoSafe from 'src/routes/SafeAppLandingPage/components/TryDemoSafe'
import UserSafe from './components/UserSafe'

const SafeAppLandingPage = (): ReactElement => {
const { search } = useLocation()
const query = new URLSearchParams(search)
const query = useQuery()
const safeAppChainId = query.get('chainId')
const safeAppUrl = query.get('appUrl')
const isValidChain = useMemo(() => isValidChainId(safeAppChainId), [safeAppChainId])

// if no valid chainId or Safe App url is present in query params we redirect to the Welcome page
// if no valid chainId is present in query params we redirect to the Welcome page
useEffect(() => {
const isValidChain = isValidChainId(safeAppChainId)
const redirectToWelcome = !safeAppUrl || !isValidChain
if (redirectToWelcome) {
history.push(WELCOME_ROUTE)
}

// we set the valid Safe App chainId in the state
if (isValidChain) {
setChainId(safeAppChainId)
setChainId(safeAppChainId as string)
}
}, [safeAppChainId, safeAppUrl])
}, [safeAppChainId, isValidChain])

// fetch Safe App details from the Config service
const { appList, isLoading: isConfigServiceLoading } = useAppList()
Expand All @@ -55,20 +50,16 @@ const SafeAppLandingPage = (): ReactElement => {

const safeAppDetails = safeAppDetailsFromConfigService || safeAppDetailsFromManifest
const isLoading = isConfigServiceLoading || isManifestLoading

// redirect to the Welcome page if the Safe App details are invalid
useEffect(() => {
const isSafeAppMissing = !isLoading && !safeAppDetails

if (isSafeAppMissing && isManifestError) {
history.push(WELCOME_ROUTE)
}
}, [isLoading, safeAppDetails, isManifestError])
const isSafeAppMissing = !isLoading && !safeAppDetails && isManifestError

const availableChains = safeAppDetails?.chainIds || []

const showLoader = isLoading || !safeAppDetails

if (!safeAppUrl || !isValidChain || isSafeAppMissing) {
return <Redirect to={WELCOME_ROUTE} />
Copy link
Contributor

Choose a reason for hiding this comment

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

This edge case is already addressed in the first useEffect right?

What is your opinion about to remove this redundant redirection and keep showing the Loader? (and restore this 2 lines in the unit tests?)

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think addressing it in useEffect is the way to go. We don't need to render anything if there's no safeAppUrl. This is also better for types stuff, because query.get returns string | undefined and this narrows it to string, making it easer for subsequent access

Copy link
Contributor

Choose a reason for hiding this comment

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

Then maybe makes sense update both useEffect ?. Something like this:

const SafeAppLandingPage = (): ReactElement => {
  const query = useQuery()
  const safeAppChainId = query.get('chainId')
  const safeAppUrl = query.get('appUrl')

  useEffect(() => {
    if (isValidChainId(safeAppChainId)) {
      setChainId(safeAppChainId)
    }
  }, [safeAppChainId])

  [...] // other stuff

  // the second useEffect is unnecessary, and instead, do:

  const isSafeAppMissing = !isLoading && !safeAppDetails && isManifestError

  if (!safeAppUrl || !safeAppChainId || isSafeAppMissing) {
    return <Redirect to={WELCOME_ROUTE} />
  }

  const availableChains = safeAppDetails?.chainIds || []

  const showLoader = isLoading || !safeAppDetails

  return (
    <Container>
      <StyledCard>
            [...]

Copy link
Member Author

Choose a reason for hiding this comment

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

done senor

}

return (
<Container>
<StyledCard>
Expand All @@ -90,7 +81,7 @@ const SafeAppLandingPage = (): ReactElement => {

<ActionsContainer>
{/* User Safe Section */}
<UserSafe />
<UserSafe safeAppUrl={safeAppUrl} />

{/* Demo Safe Section */}
<TryDemoSafe safeAppUrl={safeAppUrl} />
Expand Down
12 changes: 9 additions & 3 deletions src/routes/SafeAppLandingPage/components/UserSafe.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@ import styled from 'styled-components'
import { userAccountSelector } from 'src/logic/wallets/store/selectors'
import ConnectButton from 'src/components/ConnectButton'
import SuccessSvg from 'src/assets/icons/safe-created.svg'
import { OPEN_SAFE_ROUTE } from 'src/routes/routes'
import { OPEN_SAFE_ROUTE, SAFE_ROUTES } from 'src/routes/routes'

const UserSafe = (): ReactElement => {
type UserSafeProps = {
safeAppUrl: string
}

const UserSafe = ({ safeAppUrl }: UserSafeProps): ReactElement => {
const userAddress = useSelector(userAccountSelector)

const openSafeLink = `${OPEN_SAFE_ROUTE}?redirect=${encodeURIComponent(`${SAFE_ROUTES.APPS}?appUrl=${safeAppUrl}`)}`

const isWalletConnected = !!userAddress
return (
<UserSafeContainer>
Expand All @@ -20,7 +26,7 @@ const UserSafe = (): ReactElement => {
<>
<img alt="Vault" height={92} src={SuccessSvg} />

<StyledCreateButton size="lg" color="primary" variant="contained" component={Link} to={OPEN_SAFE_ROUTE}>
<StyledCreateButton size="lg" color="primary" variant="contained" component={Link} to={openSafeLink}>
<Text size="xl" color="white">
Create new Safe
</Text>
Expand Down
2 changes: 1 addition & 1 deletion src/routes/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export type SafeRouteSlugs = {

export const LOAD_SPECIFIC_SAFE_ROUTE = `/load/:${SAFE_ADDRESS_SLUG}?` // ? = optional slug

// Routes independant of safe/network
// Routes independent of safe/network
export const ROOT_ROUTE = '/'
export const WELCOME_ROUTE = '/welcome'
export const OPEN_SAFE_ROUTE = '/open'
Expand Down