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

feat: Safe Apps landing page #3764

Merged
merged 24 commits into from
May 12, 2022
Merged

feat: Safe Apps landing page #3764

merged 24 commits into from
May 12, 2022

Conversation

DaniSomoza
Copy link
Contributor

@DaniSomoza DaniSomoza commented Apr 6, 2022

What it solves

Resolves safe-global/safe-react-apps#414
Resolves safe-global/safe-react-apps#411

When trying to solve the problem statement of sharing a Safe App via link, we will start creating the landing page which shows the information for a Safe App, as would be the first necessary view to tackle this problem

Figma: https://www.figma.com/file/v56CKWh6Yp60cSsZ71URB0/dApp-sharing-functionality?node-id=1%3A111

How this PR fixes it

A new route is defined for the new SafeAppLandingPage component:

https://{{BASE_URL}}/app/share/safe-app?appUrl={{SAFE_APP_URL}}&chainId={{CHAIN_ID}}

How to test it

Just open a Safe App share link:

Share a Safe App defined in the config service

Tx Builder Mainnet

https://pr3764--safereact.review-safe.gnosisdev.com/app/share/safe-app?appUrl=https%3A%2F%2Fapps.gnosis-safe.io%2Ftx-builder&chainId=1

Tx Builder Rinkeby

https://pr3764--safereact.review-safe.gnosisdev.com/app/share/safe-app?appUrl=https%3A%2F%2Fapps.gnosis-safe.io%2Ftx-builder&chainId=4

Share a Safe App not defined in the config service

Safe App details loaded from the manifest.json

https://pr3764--safereact.review-safe.gnosisdev.com/app/share/safe-app?appUrl=https://pr408--safereactapps.review.gnosisdev.com/tx-builder/&chainId=1

Screenshots

Wallet Connected

Captura de pantalla 2022-04-06 a las 15 26 05

No Wallet Connected

Captura de pantalla 2022-04-06 a las 15 26 43

@github-actions
Copy link

github-actions bot commented Apr 6, 2022

CLA Assistant Lite All Contributors have signed the CLA.

@github-actions
Copy link

github-actions bot commented Apr 6, 2022

ESLint Summary View Full Report

Annotations are provided inline on the Files Changed tab. You can also see all annotations that were generated on the annotations page.

Type Occurrences Fixable
Errors 0 0
Warnings 0 0
Ignored 3 N/A
  • Result: ✅ success
  • Annotations: 0 total

Report generated by eslint-plus-action

@coveralls
Copy link

coveralls commented Apr 6, 2022

Pull Request Test Coverage Report for Build 2201347454

  • 78 of 83 (93.98%) changed or added relevant lines in 10 files are covered.
  • 249 unchanged lines in 19 files lost coverage.
  • Overall coverage increased (+0.7%) to 36.304%

Changes Missing Coverage Covered Lines Changed/Added Lines %
src/routes/index.tsx 0 1 0.0%
src/components/AppLayout/Header/components/Layout.tsx 0 2 0.0%
src/routes/SafeAppLandingPage/components/SafeAppsDetails.tsx 13 15 86.67%
Files with Coverage Reduction New Missed Lines %
src/components/AppLayout/Header/components/Layout.tsx 1 0%
src/routes/index.tsx 1 0%
src/utils/constants.ts 1 92.41%
src/components/AppLayout/Sidebar/useSidebarItems.tsx 2 0%
src/logic/addressBook/hooks/useAddressBookSync.ts 2 0%
src/components/List/ListIcon.tsx 3 0%
src/components/AppLayout/Sidebar/Threshold/index.tsx 4 0%
src/logic/safe/store/actions/utils.ts 8 75.0%
src/logic/contracts/spendingLimitContracts.ts 9 21.43%
src/logic/addressBook/store/reducer/index.ts 10 45.1%
Totals Coverage Status
Change from base Build 2147308671: 0.7%
Covered Lines: 3649
Relevant Lines: 9120

💛 - Coveralls

@github-actions
Copy link

github-actions bot commented Apr 6, 2022

Deployment links

🟠 Rinkeby Mainnet 🟣 Polygon 🟡 BSC Arbitrum 🟢 Gnosis Chain

@DaniSomoza DaniSomoza marked this pull request as ready for review April 7, 2022 09:22
Copy link
Member

@iamacook iamacook left a comment

Choose a reason for hiding this comment

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

Very cool design dude!

@@ -44,6 +44,7 @@ export const ROOT_ROUTE = '/'
export const WELCOME_ROUTE = '/welcome'
export const OPEN_SAFE_ROUTE = '/open'
export const LOAD_SAFE_ROUTE = generatePath(LOAD_SPECIFIC_SAFE_ROUTE) // By providing no slug, we get '/load'
export const SAFE_APP_LANDPAGE_ROUTE = '/share/safe-app'
Copy link
Member

Choose a reason for hiding this comment

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

I would rename this to SAFE_APP_LANDING_PAGE_ROUTE.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done!

Comment on lines 96 to 100
const { pathname } = useLocation()

const showWalletSection = !matchPath(pathname, {
path: [SAFE_APP_LANDPAGE_ROUTE],
})
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
const { pathname } = useLocation()
const showWalletSection = !matchPath(pathname, {
path: [SAFE_APP_LANDPAGE_ROUTE],
})
const showWalletSelection = !useRouteMatch({ path: SAFE_APP_LANDPAGE_ROUTE })

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done!

Comment on lines 105 to 107
const showSideBar = !matchPath(pathname, {
path: [SAFE_APP_LANDPAGE_ROUTE],
})
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
const showSideBar = !matchPath(pathname, {
path: [SAFE_APP_LANDPAGE_ROUTE],
})
const showWalletSelection = !useRouteMatch({ path: SAFE_APP_LANDPAGE_ROUTE })

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done!

Comment on lines 29 to 34
const DEMO_SAFE_MAINNET = '0xfF501B324DC6d78dC9F983f140B9211c3EdB4dc7'

const demoSafeAppsPath = generateSafeRoute(SAFE_ROUTES.APPS, {
shortName: 'eth',
safeAddress: DEMO_SAFE_MAINNET,
})
Copy link
Member

Choose a reason for hiding this comment

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

These should not be here I assume?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it is our Demo Safe. If the user clicks on Try Demo the shared Safe App should be open with that Demo Safe

Copy link
Member

Choose a reason for hiding this comment

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

I would move it to the constant file then.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done!
Added to src/routes/routes.ts


// if no valid chainId or Safe App is present in query params we redirect to the Welcome page
useEffect(() => {
const isValidChainId = safeAppChainId && getChains().find((chain) => chain.chainId === safeAppChainId)
Copy link
Member

Choose a reason for hiding this comment

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

You'll need to rename the variable, but we already have isValidChainId() in the config file.

Suggested change
const isValidChainId = safeAppChainId && getChains().find((chain) => chain.chainId === safeAppChainId)
const isValidChainId = isValidChainId(safeAppChainId)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done!

placement="bottom"
popperOptions={{ positionFixed: true }}
>
<ClickAwayListener mouseEvent="onClick" onClickAway={clickAway} touchEvent={false}>
Copy link
Member

Choose a reason for hiding this comment

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

onClick is the default prop.

Suggested change
<ClickAwayListener mouseEvent="onClick" onClickAway={clickAway} touchEvent={false}>
<ClickAwayListener onClickAway={clickAway} touchEvent={false}>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done!

</BodyImage>
{safeAppUrl && (
<StyledDemoButton
data-testid={'open-demo-app-link'}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
data-testid={'open-demo-app-link'}
data-testid='open-demo-app-link'

Copy link
Member

Choose a reason for hiding this comment

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

As before, please try to select this without using a testing ID.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done!

src/routes/SafeAppLandingPage/SafeAppLandingPage.tsx Outdated Show resolved Hide resolved
src/routes/SafeAppLandingPage/SafeAppLandingPage.tsx Outdated Show resolved Hide resolved
Comment on lines 237 to 321
const Container = styled.main`
display: flex;
justify-content: center;
margin-top: 20px;
`

const StyledCard = styled(Card)`
flex-grow: 1;
max-width: 850px;
border-radius: 20px;
padding: 50px 58px;
`

const LoaderContainer = styled.div`
display: flex;
justify-content: center;
align-items: center;
min-height: 400px;
`

const DetailsContainer = styled.div`
display: flex;
`

const SafeIcon = styled.img`
width: 90px;
height: 90px;
`

const SafeAppTitle = styled(Title)`
margin-top: 0px;
margin-bottom: 12px;
`

const DescriptionContainer = styled.div`
padding-left: 66px;
flex-grow: 1;
`

const Separator = styled(Divider)`
margin: 32px 0;
`

const ChainLabel = styled(Text)`
color: #b2bbc0;
`

const ChainsContainer = styled.div`
display: flex;
flex-wrap: wrap;

&& > div {
margin-top: 12px;
margin-right: 8px;
}
`

const ActionsContainer = styled.div`
display: flex;
`

const UserSafeContainer = styled.div`
flex: 1 0 50%;
text-align: center;
`

const SafeDemoContainer = styled.div`
flex: 1 0 50%;
text-align: center;
`
const StyledProvider = styled.div`
width: 300px;
height: 56px;
margin: 0 auto;
border-radius: 8px;
border: 2px solid #eeeff0;
`

const BodyImage = styled.div`
margin: 30px 0;
`

const StyledDemoButton = styled(Button)`
border: 2px solid #008c73;
`
Copy link
Member

Choose a reason for hiding this comment

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

A lot of the values here are exported from src/theme/variables.js. Pleaseimport them from there where appropriate instead of declaring them locally..

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done!

@mmv08
Copy link
Member

mmv08 commented Apr 7, 2022

Very excited about this feature, a couple of things I noticed:

  1. The links you shared do not show support networks like the screenshots for me
  2. Why does it not have wallet info in the header?

@DaniSomoza
Copy link
Contributor Author

Very excited about this feature, a couple of things I noticed:

  1. The links you shared do not show support networks like the screenshots for me

If the Safe App provided in the url is not present in the Config Service (in this case stage env) we are using the data from the manifest.json to show the name and the description. In this case, in the manifest.json, available chains data is not present:
using this url:

https://pr3764--safereact.review-safe.gnosisdev.com/app/share/safe-app?appUrl=https%3A%2F%2Fapps.gnosis-safe.io%2Ftx-builder&chainId=100

Captura de pantalla 2022-04-08 a las 13 39 10

But If you activate the prod env clicking on the Use prod CGW this Safe App is present in the config service response and we can show all the data:
Captura de pantalla 2022-04-08 a las 13 40 52

Captura de pantalla 2022-04-08 a las 13 43 43

  1. Why does it not have wallet info in the header?

I am not displaying it because the new designs

@mmv08
Copy link
Member

mmv08 commented Apr 8, 2022

I am not displaying it because the new designs

I found it very weird, I started a discussion on the team channel in slack

src/logic/hooks/__tests__/useAsync.test.ts Show resolved Hide resolved
Comment on lines 25 to 31
setIsLoading(false)
}
})
.catch((error) => {
if (isCurrent) setErr(error)
if (isCurrent) {
setError(error)
setIsLoading(false)
Copy link
Member

Choose a reason for hiding this comment

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

I would suggest moving setIsLoading(false) outside of asyncCall(). You won't have to set it twice.

Copy link
Member

Choose a reason for hiding this comment

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

Please keep it as it is.

Copy link
Member

Choose a reason for hiding this comment

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

Not even move it to the finally() block instead?

Copy link
Member

Choose a reason for hiding this comment

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

Finally would be good 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

at the end we will need something like:

      .finally(() => {
        if (isCurrent) {
          setIsLoading(false)
        }
      })

But ok, I will update it.


const SAFE_APP_URL_FROM_CONFIG_SERVICE = 'https://safe-app.gnosis-safe.io/test-safe-app-from-config-service'
const SAFE_APP_URL_FROM_MANIFEST = 'https://safe-app.gnosis-safe.io/test-safe-app-from-manifest'
const SAFE_APP_CHAIN_ID = '4'
Copy link
Member

Choose a reason for hiding this comment

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

You can use CHAIN_ID.RINKEBY instead of this variable.

describe('<SafeAppLandingPage>', () => {
beforeEach(() => {
// mocking safe apps list from the Config Service endpoint
jest.spyOn(safeAppsGatewaySDK, 'getSafeApps').mockReturnValue(
Copy link
Member

Choose a reason for hiding this comment

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

No need to change this but there is also mockResolvedValue(). You won't need to specify Promise.resolve().

return getAppInfoFromUrl(safeAppUrl)
}

throw 'No Safe App url provided'
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
throw 'No Safe App url provided'
throw new Error('No Safe App URL provided.')

export default SafeAppLandingPage

const SafeAppDetails = ({ iconUrl, name, description, availableChains }) => {
const showAvailableChains = availableChains && availableChains.length > 0
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
const showAvailableChains = availableChains && availableChains.length > 0
const showAvailableChains = availableChains?.length > 0

src/routes/SafeAppLandingPage/SafeAppLandingPage.tsx Outdated Show resolved Hide resolved
</StyledDemoButton>
)}
</SafeDemoContainer>
)
Copy link
Member

Choose a reason for hiding this comment

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

Thank you for splitting these components. Can you put them in separate folders in the same folder as well?

Comment on lines 173 to 201
const ConnectWallet = () => {
const { clickAway, open, toggle } = useStateHandler()

return (
<StyledProvider>
<Provider
info={<ProviderDisconnected />}
open={open}
toggle={toggle}
render={(providerRef) =>
providerRef.current && (
<StyledPopper
anchorEl={providerRef.current}
open={open}
placement="bottom"
popperOptions={{ positionFixed: true }}
>
<ClickAwayListener onClickAway={clickAway} touchEvent={false}>
<List component="div">
<ConnectDetails />
</List>
</ClickAwayListener>
</StyledPopper>
)
}
/>
</StyledProvider>
)
}
Copy link
Member

Choose a reason for hiding this comment

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

Can this be reused in the header as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We are already using all components from the header.

Copy link
Member

Choose a reason for hiding this comment

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

I mean, can you use this component in the header instead of duplicating it?

Comment on lines 128 to 133
const DEMO_SAFE_MAINNET = '0xfF501B324DC6d78dC9F983f140B9211c3EdB4dc7'

export const demoSafeRoute = generateSafeRoute(SAFE_ROUTES.APPS, {
shortName: 'eth',
safeAddress: DEMO_SAFE_MAINNET,
})
Copy link
Member

Choose a reason for hiding this comment

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

I would move the DEMO_SAFE_MAINNET to the src/utils/constants file. The demoSafeRoute can stay where it is used. Sorry, perhaps I wasn't clear before.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For some reasons when I moved the DEMO_SAFE_MAINNET to the src/utils/constants file, some units tests in the non-related googleTagManager.test.tsx file started to fail:

After some research it seems to be related with the way that this mock its defined:

  describe('loadGoogleTagManager', () => {
    it('prevents init without a gtm id/auth', () => {
      jest.doMock('src/utils/constants.ts', () => ({
        GOOGLE_TAG_MANAGER_ID: '',
        GOOGLE_TAG_MANAGER_DEVELOPMENT_AUTH: '',
      }))

     [....]

I updated them to only mock the values that are required for that tests using jest.requireActual():

      jest.doMock('src/utils/constants.ts', () => {
        const original = jest.requireActual('src/utils/constants.ts')
        return {
          ...original,
          GOOGLE_TAG_MANAGER_ID: 'id123',
          GOOGLE_TAG_MANAGER_DEVELOPMENT_AUTH: 'auth123',
        }
      })

Now it seems to be working but I think that someone should have a further look into it.

@JagoFigueroa
Copy link

Buenos días gentlemen! A couple of notes from my side:

  • I can see that the connect a wallet component goes a bit crazy on smaller screens (the video is recorded on my 16 inch macbook)
Screen.Recording.2022-04-20.at.10.15.17.mov
  • What about using a default icon when the app icon fails to be loaded like in this case?

https://pr3764--safereact.review-safe.gnosisdev.com/app/share/safe-app?appUrl=http%3A%2F%2Fgoogle.com&chainId=4

We could use the good ol'

Screenshot 2022-04-20 at 10 21 45

  • I can see some issues with the 'available networks component', a couple of examples for mainnet:

(missing bnb chain)
https://pr3764--safereact.review-safe.gnosisdev.com/app/share/safe-app?appUrl=https%3A%2F%2Fapy.plasma.finance&chainId=1

(missing the component + all chains)
https://pr3764--safereact.review-safe.gnosisdev.com/app/share/safe-app?appUrl=https%3A%2F%2Fapps.gnosis-safe.io%2Ftx-builder&chainId=1

Might there be a conflict because of having 4 chains available on staging only? should be define these differently for dev + staging?

Un saludo

@github-actions
Copy link

github-actions bot commented Apr 21, 2022

ESLint Summary View Full Report

Annotations are provided inline on the Files Changed tab. You can also see all annotations that were generated on the annotations page.

Type Occurrences Fixable
Errors 0 0
Warnings 0 0
Ignored 3 N/A
  • Result: ✅ success
  • Annotations: 0 total

Report generated by eslint-plus-action

@JagoFigueroa
Copy link

JagoFigueroa commented Apr 22, 2022

Anotha one, now thinking on the demo feature. Look at this case:

https://pr3764--safereact.review-safe.gnosisdev.com/app/share/safe-app?appUrl=https%3A%2F%2Fapp.honeyswap.org&chainId=137

Honeyswap is an app that is not available on mainnet but as demos happen on a mainnet safe, the app will open but won't work correctly. Should we do some magic for this cases? Maybe some sort or disclaimer to notify about this cases?

Edit: to reproduce it, enable prod CGW beforehand :)

@dasanra
Copy link
Collaborator

dasanra commented Apr 22, 2022

@JagoFigueroa yes, this is something we are aware of. We are still deciding how to best solve this, either creating demostration Safes for each chain or just showing a warning as you mention, because it may happen that we don't know which chains the app is available to offer an alternative

@JagoFigueroa
Copy link

All bueno, gracias guys!

DaniSomoza and others added 2 commits April 26, 2022 10:50
* Added Share Safe App button

* Added unit tests to share safe app button and moved SnackbarProvider component to our Providers file
…app-landing-page

# Conflicts:
#	src/routes/index.tsx
#	src/routes/routes.ts
@github-actions
Copy link

github-actions bot commented Apr 28, 2022

ESLint Summary View Full Report

Annotations are provided inline on the Files Changed tab. You can also see all annotations that were generated on the annotations page.

Type Occurrences Fixable
Errors 0 0
Warnings 0 0
Ignored 6 N/A
  • Result: ✅ success
  • Annotations: 0 total

Report generated by eslint-plus-action

@coveralls
Copy link

coveralls commented Apr 28, 2022

Pull Request Test Coverage Report for Build 2306310096

  • 190 of 212 (89.62%) changed or added relevant lines in 19 files are covered.
  • 4 unchanged lines in 4 files lost coverage.
  • Overall coverage increased (+1.7%) to 36.964%

Changes Missing Coverage Covered Lines Changed/Added Lines %
src/components/App/index.tsx 0 1 0.0%
src/components/AppLayout/index.tsx 0 1 0.0%
src/routes/index.tsx 0 1 0.0%
src/routes/safe/components/Apps/components/AppsList.tsx 7 8 87.5%
src/routes/SafeAppLandingPage/components/SafeAppsDetails.tsx 13 15 86.67%
src/routes/safe/components/Apps/components/SafeAppCard/SafeAppSkeleton.tsx 4 6 66.67%
src/routes/safe/components/Apps/components/SafeAppCard/SafeAppCard.tsx 35 39 89.74%
src/routes/CreateSafePage/components/SafeCreationProcess.tsx 1 11 9.09%
Files with Coverage Reduction New Missed Lines %
src/components/App/index.tsx 1 0%
src/components/AppLayout/index.tsx 1 0%
src/routes/CreateSafePage/components/SafeCreationProcess.tsx 1 7.28%
src/routes/index.tsx 1 0%
Totals Coverage Status
Change from base Build 2302058633: 1.7%
Covered Lines: 3837
Relevant Lines: 9449

💛 - Coveralls

<SafeDemoContainer>
<Title size="xs">Want to try the app before using it?</Title>

<img alt="Demo" height={92} src={DemoSvg} />
Copy link
Member

@yagopv yagopv Apr 28, 2022

Choose a reason for hiding this comment

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

WDYT about a styled component here instead setting height attr ?

// we click on the Share Safe App Button
fireEvent.click(compoundAppShareBtn)

const compaundUrl = 'https://cloudflare-ipfs.com/ipfs/QmX31xCdhFDmJzoVG33Y6kJtJ5Ujw8r5EJJBrsp8Fbjm7k'
Copy link
Member

Choose a reason for hiding this comment

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

"compoundUrl"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done!

* Added new Safe App Card Designs
const chainId = useSelector(currentChainId)
const dispatch = useDispatch()

const safeAddress = extractSafeAddress()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Before merging, let's use this hook (#3826) or I'll merge main into that PR and edit it there (depending on which one makes it through QA first).

Please leave this comment unresolved.

CC: @iamacook

:3

Copy link
Member

Choose a reason for hiding this comment

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

@DaniSomoza, this has just made it through QA. useSafeAddress now replaces extractSafeAddress.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@DaniSomoza DaniSomoza self-assigned this May 11, 2022
@DaniSomoza DaniSomoza merged commit b4a69f2 into dev May 12, 2022
@DaniSomoza DaniSomoza deleted the safe-app-landing-page branch May 12, 2022 16:03
@github-actions github-actions bot locked and limited conversation to collaborators May 12, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Safe Apps deep linking Create Safe App landing page
9 participants