mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Connected indicator info popup (#8293)
* Add popover for informing user about the connected status indicator * Ensure user only sees connected status info popover once * Default connectedStatusPopoverHasBeenShown to true and set it to false in a migration * Add unit test for migration 42 * Initialize AppStateController if it does not exist in migration 42 * Update connect indicator popup locale text * Code cleanup for connected-indicator-info-popup * Code cleanup for connected-indicator-info-popup
This commit is contained in:
parent
f2f70342e2
commit
01985b2cff
@ -22,6 +22,9 @@
|
||||
"disconnectAccountConfirmationDescription": {
|
||||
"message": "Are you sure you want to disconnect? You may lose site functionality."
|
||||
},
|
||||
"dismiss": {
|
||||
"message": "Dismiss"
|
||||
},
|
||||
"migrateSai": {
|
||||
"message": "A message from Maker: The new Multi-Collateral Dai token has been released. Your old tokens are now called Sai. Please upgrade your Sai tokens to the new Dai."
|
||||
},
|
||||
@ -867,6 +870,12 @@
|
||||
"message": {
|
||||
"message": "Message"
|
||||
},
|
||||
"metaMaskConnectStatusParagraphOne": {
|
||||
"message": "This is the new MetaMask Connect status indicator. From here you can easily see and manage sites you’ve connected to with your MetaMask wallet."
|
||||
},
|
||||
"metaMaskConnectStatusParagraphTwo": {
|
||||
"message": "Click the Connect status to see your connected sites and their permissions."
|
||||
},
|
||||
"metamaskDescription": {
|
||||
"message": "Connecting you to Ethereum and the Decentralized Web."
|
||||
},
|
||||
@ -1627,6 +1636,9 @@
|
||||
"welcome": {
|
||||
"message": "Welcome to MetaMask"
|
||||
},
|
||||
"whatsThis": {
|
||||
"message": "What's this?"
|
||||
},
|
||||
"writePhrase": {
|
||||
"message": "Write this phrase on a piece of paper and store in a secure location. If you want even more security, write it down on multiple pieces of paper and store each in 2 - 3 different locations."
|
||||
},
|
||||
|
@ -22,6 +22,7 @@ class AppStateController extends EventEmitter {
|
||||
this.store = new ObservableStore(Object.assign({
|
||||
timeoutMinutes: 0,
|
||||
mkrMigrationReminderTimestamp: null,
|
||||
connectedStatusPopoverHasBeenShown: true,
|
||||
}, initState))
|
||||
this.timer = null
|
||||
|
||||
@ -72,6 +73,15 @@ class AppStateController extends EventEmitter {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Record that the user has seen the connected status info popover
|
||||
*/
|
||||
setConnectedStatusPopoverHasBeenShown () {
|
||||
this.store.updateState({
|
||||
connectedStatusPopoverHasBeenShown: true,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the last active time to the current time
|
||||
* @returns {void}
|
||||
|
@ -511,6 +511,7 @@ export default class MetamaskController extends EventEmitter {
|
||||
// AppStateController
|
||||
setLastActiveTime: nodeify(this.appStateController.setLastActiveTime, this.appStateController),
|
||||
setMkrMigrationReminderTimestamp: nodeify(this.appStateController.setMkrMigrationReminderTimestamp, this.appStateController),
|
||||
setConnectedStatusPopoverHasBeenShown: nodeify(this.appStateController.setConnectedStatusPopoverHasBeenShown, this.appStateController),
|
||||
|
||||
// EnsController
|
||||
tryReverseResolveAddress: nodeify(this.ensController.reverseResolveAddress, this.ensController),
|
||||
|
27
app/scripts/migrations/042.js
Normal file
27
app/scripts/migrations/042.js
Normal file
@ -0,0 +1,27 @@
|
||||
const version = 42
|
||||
import { cloneDeep } from 'lodash'
|
||||
|
||||
/**
|
||||
* PreferencesController.autoLogoutTimeLimit -> autoLockTimeLimit
|
||||
*/
|
||||
export default {
|
||||
version,
|
||||
migrate: async function (originalVersionedData) {
|
||||
const versionedData = cloneDeep(originalVersionedData)
|
||||
versionedData.meta.version = version
|
||||
const state = versionedData.data
|
||||
versionedData.data = transformState(state)
|
||||
return versionedData
|
||||
},
|
||||
}
|
||||
|
||||
function transformState (state) {
|
||||
if (state.AppStateController) {
|
||||
state.AppStateController.connectedStatusPopoverHasBeenShown = false
|
||||
} else {
|
||||
state.AppStateController = {
|
||||
connectedStatusPopoverHasBeenShown: false,
|
||||
}
|
||||
}
|
||||
return state
|
||||
}
|
@ -52,6 +52,7 @@ const migrations = [
|
||||
require('./039').default,
|
||||
require('./040').default,
|
||||
require('./041').default,
|
||||
require('./042').default,
|
||||
]
|
||||
|
||||
export default migrations
|
||||
|
70
test/unit/migrations/042-test.js
Normal file
70
test/unit/migrations/042-test.js
Normal file
@ -0,0 +1,70 @@
|
||||
import assert from 'assert'
|
||||
import migration42 from '../../../app/scripts/migrations/042'
|
||||
|
||||
describe('migration #42', function () {
|
||||
|
||||
it('should update the version metadata', function (done) {
|
||||
const oldStorage = {
|
||||
'meta': {
|
||||
'version': 41,
|
||||
},
|
||||
'data': {},
|
||||
}
|
||||
|
||||
migration42.migrate(oldStorage)
|
||||
.then((newStorage) => {
|
||||
assert.deepEqual(newStorage.meta, {
|
||||
'version': 42,
|
||||
})
|
||||
done()
|
||||
})
|
||||
.catch(done)
|
||||
})
|
||||
|
||||
it('should set connectedStatusPopoverHasBeenShown to false', function (done) {
|
||||
const oldStorage = {
|
||||
meta: {},
|
||||
data: {
|
||||
AppStateController: {
|
||||
connectedStatusPopoverHasBeenShown: true,
|
||||
bar: 'baz',
|
||||
},
|
||||
foo: 'bar',
|
||||
},
|
||||
}
|
||||
|
||||
migration42.migrate(oldStorage)
|
||||
.then((newStorage) => {
|
||||
assert.deepEqual(newStorage.data, {
|
||||
AppStateController: {
|
||||
connectedStatusPopoverHasBeenShown: false,
|
||||
bar: 'baz',
|
||||
},
|
||||
foo: 'bar',
|
||||
})
|
||||
done()
|
||||
})
|
||||
.catch(done)
|
||||
})
|
||||
|
||||
it('should initialize AppStateController if it does not exist', function (done) {
|
||||
const oldStorage = {
|
||||
meta: {},
|
||||
data: {
|
||||
foo: 'bar',
|
||||
},
|
||||
}
|
||||
|
||||
migration42.migrate(oldStorage)
|
||||
.then((newStorage) => {
|
||||
assert.deepEqual(newStorage.data, {
|
||||
foo: 'bar',
|
||||
AppStateController: {
|
||||
connectedStatusPopoverHasBeenShown: false,
|
||||
},
|
||||
})
|
||||
done()
|
||||
})
|
||||
.catch(done)
|
||||
})
|
||||
})
|
@ -25,6 +25,9 @@
|
||||
display: flex;
|
||||
padding: 24px;
|
||||
flex-direction: column;
|
||||
background: white;
|
||||
position: relative;
|
||||
border-radius: 10px;
|
||||
|
||||
&__title {
|
||||
display: flex;
|
||||
@ -107,4 +110,13 @@
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
&-arrow {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
background: white;
|
||||
position: absolute;
|
||||
transform: rotate(45deg);
|
||||
box-shadow: 0px 4px 30px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
}
|
||||
|
@ -4,12 +4,27 @@ import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
import { I18nContext } from '../../../contexts/i18n'
|
||||
|
||||
const Popover = ({ title, subtitle, children, footer, footerClassName, onBack, onClose }) => {
|
||||
const Popover = ({
|
||||
title,
|
||||
subtitle = '',
|
||||
children,
|
||||
footer,
|
||||
footerClassName,
|
||||
onBack,
|
||||
onClose,
|
||||
className,
|
||||
showArrow,
|
||||
CustomBackground,
|
||||
}) => {
|
||||
const t = useContext(I18nContext)
|
||||
return (
|
||||
<div className="popover-container">
|
||||
<div className="popover-bg" onClick={onClose} />
|
||||
<section className="popover-wrap">
|
||||
{ CustomBackground
|
||||
? <CustomBackground onClose={onClose} />
|
||||
: <div className="popover-bg" onClick={onClose} />
|
||||
}
|
||||
<section className={classnames('popover-wrap', className)}>
|
||||
{ showArrow ? <div className="popover-arrow" /> : null}
|
||||
<header className="popover-header">
|
||||
<div className="popover-header__title">
|
||||
<h2 title={title}>
|
||||
@ -32,7 +47,7 @@ const Popover = ({ title, subtitle, children, footer, footerClassName, onBack, o
|
||||
onClick={onClose}
|
||||
/>
|
||||
</div>
|
||||
<p className="popover-header__subtitle">{subtitle}</p>
|
||||
{ subtitle ? <p className="popover-header__subtitle">{subtitle}</p> : null }
|
||||
</header>
|
||||
{
|
||||
children
|
||||
@ -59,12 +74,15 @@ const Popover = ({ title, subtitle, children, footer, footerClassName, onBack, o
|
||||
|
||||
Popover.propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
subtitle: PropTypes.string.isRequired,
|
||||
subtitle: PropTypes.string,
|
||||
children: PropTypes.node,
|
||||
footer: PropTypes.node,
|
||||
footerClassName: PropTypes.string,
|
||||
onBack: PropTypes.func,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
CustomBackground: PropTypes.func,
|
||||
className: PropTypes.string,
|
||||
showArrow: PropTypes.bool,
|
||||
}
|
||||
|
||||
export default class PopoverPortal extends PureComponent {
|
||||
|
@ -11,6 +11,8 @@ import WalletView from '../../components/app/wallet-view'
|
||||
import TransactionList from '../../components/app/transaction-list'
|
||||
import TransactionViewBalance from '../../components/app/transaction-view-balance'
|
||||
import MenuBar from '../../components/app/menu-bar'
|
||||
import Popover from '../../components/ui/popover'
|
||||
import Button from '../../components/ui/button'
|
||||
import ConnectedSites from '../connected-sites'
|
||||
import { Tabs, Tab } from '../../components/ui/tabs'
|
||||
|
||||
@ -51,6 +53,8 @@ export default class Home extends PureComponent {
|
||||
hasDaiV1Token: PropTypes.bool,
|
||||
firstPermissionsRequestId: PropTypes.string,
|
||||
totalUnapprovedCount: PropTypes.number.isRequired,
|
||||
setConnectedStatusPopoverHasBeenShown: PropTypes.func,
|
||||
connectedStatusPopoverHasBeenShown: PropTypes.bool,
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount () {
|
||||
@ -172,11 +176,48 @@ export default class Home extends PureComponent {
|
||||
</MultipleNotifications>
|
||||
)
|
||||
}
|
||||
renderPopover = () => {
|
||||
const { setConnectedStatusPopoverHasBeenShown } = this.props
|
||||
const { t } = this.context
|
||||
return (
|
||||
<Popover
|
||||
title={ t('whatsThis') }
|
||||
onClose={setConnectedStatusPopoverHasBeenShown}
|
||||
className="home__connected-status-popover"
|
||||
showArrow
|
||||
CustomBackground={({ onClose }) => {
|
||||
return (
|
||||
<div
|
||||
className="home__connected-status-popover-bg-container"
|
||||
onClick={onClose}
|
||||
>
|
||||
<div className="home__connected-status-popover-bg" />
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
footer={(
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={setConnectedStatusPopoverHasBeenShown}
|
||||
>
|
||||
{ t('dismiss') }
|
||||
</Button>
|
||||
)}
|
||||
>
|
||||
<main className="home__connect-status-text">
|
||||
<div>{ t('metaMaskConnectStatusParagraphOne') }</div>
|
||||
<div>{ t('metaMaskConnectStatusParagraphTwo') }</div>
|
||||
</main>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
const {
|
||||
forgottenPassword,
|
||||
history,
|
||||
connectedStatusPopoverHasBeenShown,
|
||||
isPopup,
|
||||
} = this.props
|
||||
|
||||
if (forgottenPassword) {
|
||||
@ -191,6 +232,7 @@ export default class Home extends PureComponent {
|
||||
<div className="main-container">
|
||||
<Route path={CONNECTED_ROUTE} component={ConnectedSites} />
|
||||
<div className="home__container">
|
||||
{ isPopup && !connectedStatusPopoverHasBeenShown ? this.renderPopover() : null }
|
||||
<Media
|
||||
query="(min-width: 576px)"
|
||||
>
|
||||
|
@ -16,6 +16,7 @@ import {
|
||||
turnThreeBoxSyncingOn,
|
||||
getThreeBoxLastUpdated,
|
||||
setShowRestorePromptToFalse,
|
||||
setConnectedStatusPopoverHasBeenShown,
|
||||
} from '../../store/actions'
|
||||
import { setThreeBoxLastUpdated } from '../../ducks/app/app'
|
||||
import { getEnvironmentType } from '../../../../app/scripts/lib/util'
|
||||
@ -33,6 +34,7 @@ const mapStateToProps = (state) => {
|
||||
threeBoxSynced,
|
||||
showRestorePrompt,
|
||||
selectedAddress,
|
||||
connectedStatusPopoverHasBeenShown,
|
||||
} = metamask
|
||||
const accountBalance = getCurrentEthBalance(state)
|
||||
const { forgottenPassword, threeBoxLastUpdated } = appState
|
||||
@ -61,6 +63,7 @@ const mapStateToProps = (state) => {
|
||||
hasDaiV1Token: Boolean(getDaiV1Token(state)),
|
||||
firstPermissionsRequestId,
|
||||
totalUnapprovedCount,
|
||||
connectedStatusPopoverHasBeenShown,
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,6 +82,7 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
},
|
||||
restoreFromThreeBox: (address) => dispatch(restoreFromThreeBox(address)),
|
||||
setShowRestorePromptToFalse: () => dispatch(setShowRestorePromptToFalse()),
|
||||
setConnectedStatusPopoverHasBeenShown: () => dispatch(setConnectedStatusPopoverHasBeenShown()),
|
||||
})
|
||||
|
||||
export default compose(
|
||||
|
@ -36,4 +36,69 @@
|
||||
&__tab {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
&__connect-status-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@extend %content-text;
|
||||
padding-left: 24px;
|
||||
padding-right: 24px;
|
||||
|
||||
div {
|
||||
&:last-child {
|
||||
margin-top: 26px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__connected-status-popover {
|
||||
width: 329px;
|
||||
height: 295px;
|
||||
margin-top: -12px;
|
||||
|
||||
.popover-header {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.popover-content {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.popover-arrow {
|
||||
top: -6px;
|
||||
left: 24px;
|
||||
}
|
||||
|
||||
.popover-footer {
|
||||
justify-content: flex-end;
|
||||
|
||||
& :only-child {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
button {
|
||||
height: 39px;
|
||||
width: 133px;
|
||||
border-radius: 39px;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__connected-status-popover-bg {
|
||||
height: 34px;
|
||||
width: 110px;
|
||||
border-radius: 34px;
|
||||
position: absolute;
|
||||
top: 82px;
|
||||
left: 12px;
|
||||
opacity: 1;
|
||||
box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.2);
|
||||
background: none;
|
||||
}
|
||||
|
||||
&__connected-status-popover-bg-container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
@ -2208,6 +2208,16 @@ export function setMkrMigrationReminderTimestamp (timestamp) {
|
||||
}
|
||||
}
|
||||
|
||||
export function setConnectedStatusPopoverHasBeenShown () {
|
||||
return () => {
|
||||
background.setConnectedStatusPopoverHasBeenShown((err) => {
|
||||
if (err) {
|
||||
throw new Error(err.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function loadingMethoDataStarted () {
|
||||
return {
|
||||
type: actionConstants.LOADING_METHOD_DATA_STARTED,
|
||||
|
Loading…
Reference in New Issue
Block a user