Skip to content

Commit

Permalink
update mobile best practices (MetaMask#118)
Browse files Browse the repository at this point in the history
  • Loading branch information
rekmarks authored Jun 25, 2020
1 parent 20ae87f commit 8319fe4
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 53 deletions.
11 changes: 6 additions & 5 deletions docs/guide/ethereum-provider.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,18 +140,19 @@ Requests to view the user's Ethereum address.
#### Example

```javascript
ethereum.sendAsync({
ethereum.sendAsync(
{
method: 'eth_requestAccounts',
},
(error, response) => {
if (error) {
// Handle error. Likely the user rejected the login
console.error(error);
} else {
const accounts = response.result
// You now have an array of accounts!
// Currently only ever one, e.g.:
// ['0xFDEa65C8e26263F6d9A1B5de9555D2931A33b825']
const accounts = response.result;
// You now have an array of accounts!
// Currently only ever one, e.g.:
// ['0xFDEa65C8e26263F6d9A1B5de9555D2931A33b825']
}
}
);
Expand Down
25 changes: 14 additions & 11 deletions docs/guide/mobile-best-practices.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
# Mobile Best Practices

If your site works with the MetaMask Extension, then you should be all set!
If it doesn't, please see below.
If you're still having issues, feel free to open an issue [here](https://github.com/MetaMask/metamask-mobile)
If this page doesn't answer your question, please feel free to open an issue [in our repository](https://github.com/MetaMask/metamask-mobile).

# ethereum.initialized
## The Provider (window.ethereum)

On mobile, the loading of the window can be slower than you might be used to dealing with on the web. Because of this, we've implemented an event: `ethereum#initialized`
The [provider API](./ethereum-provider.html) is the same for both MetaMask mobile and the desktop extension.
However, the providers become available (i.e., are injected into the page) at different points in the page lifecycle.

[see gist](https://gist.github.com/rekmarks/06999f88fe6ab0cd1d71ac7cd2b2ac93)
### Provider Availability

New event dispatched on `window`: `ethereum#initialized`
If you use [`@metamask/detect-provider`](https://npmjs.com/package/@metamask/detect-provider), there's nothing to worry about; it will reliably detect both the mobile and extension provider.

Event name inspired by JSDoc `@event` tag: https://jsdoc.app/tags-event.html
If you don't use the `detect-provider` package, you have to detect the mobile provider manually.

The extension provider will always be available by the time your code is executed.
Because of platform limitations, the mobile provider may not be injected until later in the page lifecycle.
For this purpose, the MetaMask provider dispatches the event `ethereum#initialized` on `window` when it is fully initialized.

You can reliably detect both the mobile and extension provider with the following snippet.

```javascript
if (window.ethereum) {
Expand All @@ -31,11 +36,9 @@ function handleEthereum() {
const { ethereum } = window;
if (ethereum && ethereum.isMetaMask) {
console.log('Ethereum successfully detected!');
// Do work...
// Access the decentralized web!
} else {
console.log('Please install MetaMask!');
}
}
```

But for the best user experience, we would also like to encourage the practice of only asking for the user's accounts upon a user initiated interaction.
88 changes: 51 additions & 37 deletions docs/guide/onboarding-library.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# Onboarding Library

As an Ethereum enabled site developer, sending users offsite to install MetaMask presents challenges. Most notably, you must inform the user to return to your site and refresh their browser after the installation. Your site will detect the user's newly installed MetaMask extension only after that refresh. We at MetaMask care deeply about user experience, and we knew that this workflow needed to be improved.

MetaMask now provides a [metamask-onboarding library](https://github.com/MetaMask/metamask-onboarding) designed to improve and simplify the onboarding experience. The new library exposes an API to initiate the onboarding process. In the process, it registers your site as the origin of the onboarding request. MetaMask will check for this origin after the user completes the onboarding flow. If it finds an origin, the final confirmation button of the MetaMask onboarding flow will indicate that the user will be redirected back to your site.

## Getting Started

1. Install @metamask/onboarding using npm or yarn.
2. Import the Onboarding Library or include it in your page.

Expand All @@ -15,8 +17,9 @@ const MetamaskOnboarding = require('@metamask/onboarding');
```

If you'd prefer you can instead include the prebuilt ES5 bundle that ships with the library:

```html
<script type="text/javascript" src="./metamask-onboarding.bundle.js"></script>
<script type="text/javascript" src="./metamask-onboarding.bundle.js"></script>
```

3. Create a new instance of the Onboarding library
Expand All @@ -32,13 +35,16 @@ onboarding.startOnboarding();
```

## Examples

### Basic Usage

```javascript
const onboarding = new MetamaskOnboarding();
onboarding.startOnboarding();
```

### Using React

```jsx
import MetaMaskOnboarding from '@metamask/onboarding';
import React from 'react';
Expand All @@ -57,7 +63,7 @@ export function OnboardingButton() {
if (!onboarding.current) {
onboarding.current = new MetaMaskOnboarding();
}
}, [])
}, []);

React.useEffect(() => {
if (MetaMaskOnboarding.isMetaMaskInstalled()) {
Expand All @@ -77,30 +83,35 @@ export function OnboardingButton() {
setAccounts(newAccounts);
}
if (MetaMaskOnboarding.isMetaMaskInstalled()) {
window.ethereum.request({ method: 'eth_requestAccounts' })
window.ethereum
.request({ method: 'eth_requestAccounts' })
.then(handleNewAccounts);
window.ethereum.on('accountsChanged', handleNewAccounts)
window.ethereum.on('accountsChanged', handleNewAccounts);
return () => {
window.ethereum.off('accountsChanged', handleNewAccounts)
}
window.ethereum.off('accountsChanged', handleNewAccounts);
};
}
}, []);

const onClick = () => {
if (MetaMaskOnboarding.isMetaMaskInstalled()) {
window.ethereum.request({ method: 'eth_requestAccounts' })
.then(newAccounts => setAccounts(newAccounts));
window.ethereum
.request({ method: 'eth_requestAccounts' })
.then((newAccounts) => setAccounts(newAccounts));
} else {
onboarding.current.startOnboarding();
}
}
};
return (
<button disabled={isDisabled} onClick={onClick}>{buttonText}</button>
)
<button disabled={isDisabled} onClick={onClick}>
{buttonText}
</button>
);
}
```

### Using TypeScript

We ship our TypeScript types with `@metamask/onboarding`. Modifying the above example to get type safety when using the onboarding library is simple:

```jsx
Expand All @@ -112,60 +123,63 @@ Doing this step will give you editor auto-completion for the methods exposed by

![Editor Highlighting](https://user-images.githubusercontent.com/4448075/85584481-ccc7ec00-b604-11ea-9b74-49c76ee0bf22.png)


### Using Vanilla Javascript + HTML

```html
<html>
<head>
<meta charset="UTF-8">
<meta charset="UTF-8" />
</head>
<body>
<h1>Sample Dapp</h1>
<button id='onboard'>Loading...</button>
<script type="text/javascript" src="./metamask-onboarding.bundle.js"></script>
<button id="onboard">Loading...</button>
<script
type="text/javascript"
src="./metamask-onboarding.bundle.js"
></script>
<script type="text/javascript">
window.addEventListener('DOMContentLoaded', () => {
const onboarding = new MetamaskOnboarding()
const onboardButton = document.getElementById('onboard')
let accounts
const onboarding = new MetamaskOnboarding();
const onboardButton = document.getElementById('onboard');
let accounts;
const updateButton = () => {
if (!MetamaskOnboarding.isMetaMaskInstalled()) {
onboardButton.innerText = 'Click here to install MetaMask!'
onboardButton.innerText = 'Click here to install MetaMask!';
onboardButton.onclick = () => {
onboardButton.innerText = 'Onboarding in progress'
onboardButton.disabled = true
onboarding.startOnboarding()
}
onboardButton.innerText = 'Onboarding in progress';
onboardButton.disabled = true;
onboarding.startOnboarding();
};
} else if (accounts && accounts.length > 0) {
onboardButton.innerText = 'Connected'
onboardButton.disabled = true
onboarding.stopOnboarding()
onboardButton.innerText = 'Connected';
onboardButton.disabled = true;
onboarding.stopOnboarding();
} else {
onboardButton.innerText = 'Connect'
onboardButton.innerText = 'Connect';
onboardButton.onclick = async () => {
await window.ethereum.request({
method: 'eth_requestAccounts'
})
}
method: 'eth_requestAccounts',
});
};
}
}
};
updateButton()
updateButton();
if (MetamaskOnboarding.isMetaMaskInstalled()) {
window.ethereum.on('accountsChanged', (newAccounts) => {
accounts = newAccounts
updateButton()
})
accounts = newAccounts;
updateButton();
});
}
})
});
</script>
</body>
</html>
```


## Onboarding Diagram

Here is a diagram of the interactions between the onboarding library, the forwarder, and the extension:

![Onboarding Library Diagram](https://user-images.githubusercontent.com/2459287/67541693-439c9600-f6c0-11e9-93f8-112a8941384a.png)

0 comments on commit 8319fe4

Please sign in to comment.