diff options
author | Whymarrh Whitby <whymarrh.whitby@gmail.com> | 2019-08-16 05:07:18 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-08-16 05:07:18 +0800 |
commit | 247659ca651b683baba4eff2fbb49a9cb89d4503 (patch) | |
tree | 59df94c5a34a8316d5ddbcd941fa44057f111350 /ui/app | |
parent | 4d9b095dd0c280638b7593e87317f075b93f5ec4 (diff) | |
download | tangerine-wallet-browser-247659ca651b683baba4eff2fbb49a9cb89d4503.tar.gz tangerine-wallet-browser-247659ca651b683baba4eff2fbb49a9cb89d4503.tar.zst tangerine-wallet-browser-247659ca651b683baba4eff2fbb49a9cb89d4503.zip |
Connections settings tab (#7013)
* Nix notification for Share Address
* Add Connections settings tab in place of privacy mode toggle
* Split ProviderApprovalController into two stores
* Remove privacyMode feature flag altogether
* Add migration to remove privacyMode feature flag
Diffstat (limited to 'ui/app')
16 files changed, 235 insertions, 163 deletions
diff --git a/ui/app/helpers/constants/routes.js b/ui/app/helpers/constants/routes.js index cd26b3628..8c2a8b8bc 100644 --- a/ui/app/helpers/constants/routes.js +++ b/ui/app/helpers/constants/routes.js @@ -3,6 +3,7 @@ const UNLOCK_ROUTE = '/unlock' const LOCK_ROUTE = '/lock' const SETTINGS_ROUTE = '/settings' const GENERAL_ROUTE = '/settings/general' +const CONNECTIONS_ROUTE = '/settings/connections' const ADVANCED_ROUTE = '/settings/advanced' const SECURITY_ROUTE = '/settings/security' const ABOUT_US_ROUTE = '/settings/about-us' @@ -82,6 +83,7 @@ module.exports = { ADVANCED_ROUTE, SECURITY_ROUTE, GENERAL_ROUTE, + CONNECTIONS_ROUTE, ABOUT_US_ROUTE, CONTACT_LIST_ROUTE, CONTACT_EDIT_ROUTE, @@ -93,4 +95,3 @@ module.exports = { NETWORKS_ROUTE, INITIALIZE_BACKUP_SEED_PHRASE_ROUTE, } - diff --git a/ui/app/pages/home/home.component.js b/ui/app/pages/home/home.component.js index 66d962ff1..ff7428eff 100644 --- a/ui/app/pages/home/home.component.js +++ b/ui/app/pages/home/home.component.js @@ -21,18 +21,10 @@ export default class Home extends PureComponent { } static defaultProps = { - activeTab: {}, unsetMigratedPrivacyMode: null, - forceApproveProviderRequestByOrigin: null, } static propTypes = { - activeTab: PropTypes.shape({ - origin: PropTypes.string, - protocol: PropTypes.string, - title: PropTypes.string, - url: PropTypes.string, - }), history: PropTypes.object, forgottenPassword: PropTypes.bool, suggestedTokens: PropTypes.object, @@ -40,10 +32,7 @@ export default class Home extends PureComponent { providerRequests: PropTypes.array, showPrivacyModeNotification: PropTypes.bool.isRequired, unsetMigratedPrivacyMode: PropTypes.func, - viewingUnconnectedDapp: PropTypes.bool.isRequired, - forceApproveProviderRequestByOrigin: PropTypes.func, shouldShowSeedPhraseReminder: PropTypes.bool, - rejectProviderRequestByOrigin: PropTypes.func, isPopup: PropTypes.bool, } @@ -73,16 +62,12 @@ export default class Home extends PureComponent { render () { const { t } = this.context const { - activeTab, forgottenPassword, providerRequests, history, showPrivacyModeNotification, unsetMigratedPrivacyMode, - viewingUnconnectedDapp, - forceApproveProviderRequestByOrigin, shouldShowSeedPhraseReminder, - rejectProviderRequestByOrigin, isPopup, } = this.props @@ -121,20 +106,6 @@ export default class Home extends PureComponent { />, }, { - shouldBeRendered: viewingUnconnectedDapp, - component: <HomeNotification - descriptionText={t('shareAddressToConnect', [activeTab.origin])} - acceptText={t('shareAddress')} - onAccept={() => { - forceApproveProviderRequestByOrigin(activeTab.origin) - }} - ignoreText={t('dismiss')} - onIgnore={() => rejectProviderRequestByOrigin(activeTab.origin)} - infoText={t('shareAddressInfo', [activeTab.origin])} - key="home-shareAddressToConnect" - />, - }, - { shouldBeRendered: shouldShowSeedPhraseReminder, component: <HomeNotification descriptionText={t('backupApprovalNotice')} diff --git a/ui/app/pages/home/home.container.js b/ui/app/pages/home/home.container.js index fdcb3ded4..4de51ee50 100644 --- a/ui/app/pages/home/home.container.js +++ b/ui/app/pages/home/home.container.js @@ -5,39 +5,23 @@ import { withRouter } from 'react-router-dom' import { unconfirmedTransactionsCountSelector } from '../../selectors/confirm-transaction' import { getCurrentEthBalance } from '../../selectors/selectors' import { - forceApproveProviderRequestByOrigin, unsetMigratedPrivacyMode, - rejectProviderRequestByOrigin, } from '../../store/actions' import { getEnvironmentType } from '../../../../app/scripts/lib/util' import { ENVIRONMENT_TYPE_POPUP } from '../../../../app/scripts/lib/enums' -const activeTabDappProtocols = ['http:', 'https:', 'dweb:', 'ipfs:', 'ipns:', 'ssb:'] - const mapStateToProps = state => { - const { activeTab, metamask, appState } = state + const { metamask, appState } = state const { - approvedOrigins, - dismissedOrigins, suggestedTokens, providerRequests, migratedPrivacyMode, - featureFlags: { - privacyMode, - } = {}, seedPhraseBackedUp, tokens, } = metamask const accountBalance = getCurrentEthBalance(state) const { forgottenPassword } = appState - const isUnconnected = Boolean( - activeTab && - activeTabDappProtocols.includes(activeTab.protocol) && - privacyMode && - !approvedOrigins[activeTab.origin] && - !dismissedOrigins[activeTab.origin] - ) const isPopup = getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_POPUP return { @@ -46,8 +30,6 @@ const mapStateToProps = state => { unconfirmedTransactionsCount: unconfirmedTransactionsCountSelector(state), providerRequests, showPrivacyModeNotification: migratedPrivacyMode, - activeTab, - viewingUnconnectedDapp: isUnconnected && isPopup, shouldShowSeedPhraseReminder: !seedPhraseBackedUp && (parseInt(accountBalance, 16) > 0 || tokens.length > 0), isPopup, } @@ -55,8 +37,6 @@ const mapStateToProps = state => { const mapDispatchToProps = (dispatch) => ({ unsetMigratedPrivacyMode: () => dispatch(unsetMigratedPrivacyMode()), - forceApproveProviderRequestByOrigin: (origin) => dispatch(forceApproveProviderRequestByOrigin(origin)), - rejectProviderRequestByOrigin: origin => dispatch(rejectProviderRequestByOrigin(origin)), }) export default compose( diff --git a/ui/app/pages/settings/connections-tab/connected-site-row/connected-site-row.component.js b/ui/app/pages/settings/connections-tab/connected-site-row/connected-site-row.component.js new file mode 100644 index 000000000..37c068ab5 --- /dev/null +++ b/ui/app/pages/settings/connections-tab/connected-site-row/connected-site-row.component.js @@ -0,0 +1,31 @@ +import React, { PureComponent } from 'react' +import PropTypes from 'prop-types' + +export default class ConnectedSiteRow extends PureComponent { + static defaultProps = { + siteTitle: null, + siteImage: null, + onDelete: () => {}, + } + + static propTypes = { + siteTitle: PropTypes.string, + siteImage: PropTypes.string, + origin: PropTypes.string.isRequired, + onDelete: PropTypes.func, + } + + render () { + const { + origin, + onDelete, + } = this.props + + return ( + <div className="connected-site-row"> + <div className="connected-site-row__origin">{origin}</div> + <div className="connected-site-row__delete" onClick={onDelete}><i className="fa fa-trash" /></div> + </div> + ) + } +} diff --git a/ui/app/pages/settings/connections-tab/connected-site-row/index.js b/ui/app/pages/settings/connections-tab/connected-site-row/index.js new file mode 100644 index 000000000..57023b6da --- /dev/null +++ b/ui/app/pages/settings/connections-tab/connected-site-row/index.js @@ -0,0 +1 @@ +export { default } from './connected-site-row.component' diff --git a/ui/app/pages/settings/connections-tab/connected-site-row/index.scss b/ui/app/pages/settings/connections-tab/connected-site-row/index.scss new file mode 100644 index 000000000..f0957a8e9 --- /dev/null +++ b/ui/app/pages/settings/connections-tab/connected-site-row/index.scss @@ -0,0 +1,14 @@ +.connected-site-row { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + + &__origin { + font-family: monospace; + } + + &__delete { + padding: 8px; + } +} diff --git a/ui/app/pages/settings/connections-tab/connections-tab.component.js b/ui/app/pages/settings/connections-tab/connections-tab.component.js new file mode 100644 index 000000000..3309fcc0d --- /dev/null +++ b/ui/app/pages/settings/connections-tab/connections-tab.component.js @@ -0,0 +1,133 @@ +import React, { PureComponent } from 'react' +import PropTypes from 'prop-types' +import ConnectedSiteEntry from './connected-site-row' +import TextField from '../../../components/ui/text-field' +import Button from '../../../components/ui/button' + +export default class ConnectionsTab extends PureComponent { + static contextTypes = { + t: PropTypes.func, + metricsEvent: PropTypes.func, + } + + static defaultProps = { + activeTab: {}, + } + + static propTypes = { + activeTab: PropTypes.object, + approvedOrigins: PropTypes.object.isRequired, + approveProviderRequestByOrigin: PropTypes.func.isRequired, + rejectProviderRequestByOrigin: PropTypes.func.isRequired, + showClearApprovalModal: PropTypes.func.isRequired, + } + + state = { + input: this.props.activeTab.origin || '', + } + + handleAddOrigin = () => { + const newOrigin = this.state.input + this.setState({ + input: '', + }, () => { + if (newOrigin && newOrigin.trim()) { + this.props.approveProviderRequestByOrigin(newOrigin) + } + }) + } + + renderNewOriginInput () { + const { t } = this.context + + return ( + <div className="settings-page__content-row"> + <div className="settings-page__content-item"> + <span>{ t('addSite') }</span> + <div className="settings-page__content-description"> + { t('addSiteDescription') } + </div> + </div> + <div className="settings-page__content-item"> + <div className="settings-page__content-item-col"> + <TextField + type="text" + value={this.state.input} + onChange={e => this.setState({ input: e.target.value })} + fullWidth + margin="dense" + min={0} + /> + <button + className="button btn-primary settings-tab__rpc-save-button" + onClick={this.handleAddOrigin} + > + { t('connect') } + </button> + </div> + </div> + </div> + ) + } + + renderApprovedOriginsList () { + const { t } = this.context + const { approvedOrigins, rejectProviderRequestByOrigin, showClearApprovalModal } = this.props + const approvedEntries = Object.entries(approvedOrigins) + const approvalListEmpty = approvedEntries.length === 0 + + return ( + <div className="settings-page__content-row"> + <div className="settings-page__content-item"> + <span>{ t('connected') }</span> + <span className="settings-page__content-description"> + { t('connectedDescription') } + </span> + </div> + <div className="settings-page__content-item"> + { + approvalListEmpty + ? <div><i className="fa fa-ban" /></div> + : null + } + { + approvedEntries.map(([origin, { siteTitle, siteImage }]) => ( + <ConnectedSiteEntry + key={origin} + origin={origin} + siteTitle={siteTitle} + siteImage={siteImage} + onDelete={() => { + rejectProviderRequestByOrigin(origin) + }} + /> + )) + } + </div> + <div className="settings-page__content-item-col"> + <Button + disabled={approvalListEmpty} + type="warning" + large + className="settings-tab__button--orange" + onClick={event => { + event.preventDefault() + showClearApprovalModal() + }} + > + { t('clearApprovalData') } + </Button> + </div> + </div> + ) + } + + render () { + return ( + <div className="settings-page__body"> + { this.renderNewOriginInput() } + { this.renderApprovedOriginsList() } + </div> + ) + } +} diff --git a/ui/app/pages/settings/connections-tab/connections-tab.container.js b/ui/app/pages/settings/connections-tab/connections-tab.container.js new file mode 100644 index 000000000..cf3efc2b4 --- /dev/null +++ b/ui/app/pages/settings/connections-tab/connections-tab.container.js @@ -0,0 +1,39 @@ +import ConnectionsTab from './connections-tab.component' +import { compose } from 'recompose' +import { connect } from 'react-redux' +import { withRouter } from 'react-router-dom' +import { + approveProviderRequestByOrigin, + rejectProviderRequestByOrigin, + showModal, +} from '../../../store/actions' + +export const mapStateToProps = state => { + const { + activeTab, + metamask, + } = state + const { + approvedOrigins, + } = metamask + + return { + activeTab, + approvedOrigins, + } +} + +export const mapDispatchToProps = dispatch => { + return { + approveProviderRequestByOrigin: (origin) => dispatch(approveProviderRequestByOrigin(origin)), + rejectProviderRequestByOrigin: (origin) => dispatch(rejectProviderRequestByOrigin(origin)), + showClearApprovalModal: () => dispatch(showModal({ + name: 'CLEAR_APPROVED_ORIGINS', + })), + } +} + +export default compose( + withRouter, + connect(mapStateToProps, mapDispatchToProps) +)(ConnectionsTab) diff --git a/ui/app/pages/settings/connections-tab/index.js b/ui/app/pages/settings/connections-tab/index.js new file mode 100644 index 000000000..b04f4e33a --- /dev/null +++ b/ui/app/pages/settings/connections-tab/index.js @@ -0,0 +1 @@ +export { default } from './connections-tab.container' diff --git a/ui/app/pages/settings/connections-tab/index.scss b/ui/app/pages/settings/connections-tab/index.scss new file mode 100644 index 000000000..249a7193f --- /dev/null +++ b/ui/app/pages/settings/connections-tab/index.scss @@ -0,0 +1 @@ +@import './connected-site-row/index'; diff --git a/ui/app/pages/settings/index.scss b/ui/app/pages/settings/index.scss index 73f36806d..4cbe5632e 100644 --- a/ui/app/pages/settings/index.scss +++ b/ui/app/pages/settings/index.scss @@ -4,6 +4,8 @@ @import 'settings-tab/index'; +@import 'connections-tab/index'; + @import 'contact-list-tab/index'; .settings-page { diff --git a/ui/app/pages/settings/security-tab/security-tab.component.js b/ui/app/pages/settings/security-tab/security-tab.component.js index 0d367abfb..600ec7069 100644 --- a/ui/app/pages/settings/security-tab/security-tab.component.js +++ b/ui/app/pages/settings/security-tab/security-tab.component.js @@ -1,6 +1,5 @@ import React, { PureComponent } from 'react' import PropTypes from 'prop-types' -import { exportAsFile } from '../../../helpers/utils/util' import ToggleButton from '../../../components/ui/toggle-button' import { REVEAL_SEED_ROUTE } from '../../../helpers/constants/routes' import Button from '../../../components/ui/button' @@ -12,11 +11,8 @@ export default class SecurityTab extends PureComponent { } static propTypes = { - setPrivacyMode: PropTypes.func, - privacyMode: PropTypes.bool, displayWarning: PropTypes.func, revealSeedConfirmation: PropTypes.func, - showClearApprovalModal: PropTypes.func, warning: PropTypes.string, history: PropTypes.object, mobileSync: PropTypes.bool, @@ -24,71 +20,6 @@ export default class SecurityTab extends PureComponent { setParticipateInMetaMetrics: PropTypes.func, } - renderStateLogs () { - const { t } = this.context - const { displayWarning } = this.props - - return ( - <div className="settings-page__content-row"> - <div className="settings-page__content-item"> - <span>{ t('stateLogs') }</span> - <span className="settings-page__content-description"> - { t('stateLogsDescription') } - </span> - </div> - <div className="settings-page__content-item"> - <div className="settings-page__content-item-col"> - <Button - type="secondary" - large - onClick={() => { - window.logStateString((err, result) => { - if (err) { - displayWarning(t('stateLogError')) - } else { - exportAsFile('MetaMask State Logs.json', result) - } - }) - }} - > - { t('downloadStateLogs') } - </Button> - </div> - </div> - </div> - ) - } - - renderClearApproval () { - const { t } = this.context - const { showClearApprovalModal } = this.props - return ( - <div className="settings-page__content-row"> - <div className="settings-page__content-item"> - <span>{ t('approvalData') }</span> - <span className="settings-page__content-description"> - { t('approvalDataDescription') } - </span> - </div> - <div className="settings-page__content-item"> - <div className="settings-page__content-item-col"> - <Button - type="warning" - large - className="settings-tab__button--orange" - onClick={event => { - event.preventDefault() - showClearApprovalModal() - }} - > - { t('clearApprovalData') } - </Button> - </div> - </div> - </div> - ) - } - renderSeedWords () { const { t } = this.context const { history } = this.props @@ -123,32 +54,6 @@ export default class SecurityTab extends PureComponent { ) } - renderPrivacyOptIn () { - const { t } = this.context - const { privacyMode, setPrivacyMode } = this.props - - return ( - <div className="settings-page__content-row"> - <div className="settings-page__content-item"> - <span>{ t('privacyMode') }</span> - <div className="settings-page__content-description"> - { t('privacyModeDescription') } - </div> - </div> - <div className="settings-page__content-item"> - <div className="settings-page__content-item-col"> - <ToggleButton - value={privacyMode} - onToggle={value => setPrivacyMode(!value)} - offLabel={t('off')} - onLabel={t('on')} - /> - </div> - </div> - </div> - ) - } - renderMetaMetricsOptIn () { const { t } = this.context const { participateInMetaMetrics, setParticipateInMetaMetrics } = this.props @@ -181,8 +86,6 @@ export default class SecurityTab extends PureComponent { return ( <div className="settings-page__body"> { warning && <div className="settings-tab__error">{ warning }</div> } - { this.renderPrivacyOptIn() } - { this.renderClearApproval() } { this.renderSeedWords() } { this.renderMetaMetricsOptIn() } </div> diff --git a/ui/app/pages/settings/security-tab/security-tab.container.js b/ui/app/pages/settings/security-tab/security-tab.container.js index 6036f4eda..fe79d86af 100644 --- a/ui/app/pages/settings/security-tab/security-tab.container.js +++ b/ui/app/pages/settings/security-tab/security-tab.container.js @@ -5,23 +5,17 @@ import { withRouter } from 'react-router-dom' import { displayWarning, revealSeedConfirmation, - setFeatureFlag, - showModal, setParticipateInMetaMetrics, } from '../../../store/actions' const mapStateToProps = state => { const { appState: { warning }, metamask } = state const { - featureFlags: { - privacyMode, - } = {}, participateInMetaMetrics, } = metamask return { warning, - privacyMode, participateInMetaMetrics, } } @@ -30,8 +24,6 @@ const mapDispatchToProps = dispatch => { return { displayWarning: warning => dispatch(displayWarning(warning)), revealSeedConfirmation: () => dispatch(revealSeedConfirmation()), - setPrivacyMode: enabled => dispatch(setFeatureFlag('privacyMode', enabled)), - showClearApprovalModal: () => dispatch(showModal({ name: 'CLEAR_APPROVED_ORIGINS' })), setParticipateInMetaMetrics: (val) => dispatch(setParticipateInMetaMetrics(val)), } } diff --git a/ui/app/pages/settings/settings.component.js b/ui/app/pages/settings/settings.component.js index 55fbebfac..781b38eff 100644 --- a/ui/app/pages/settings/settings.component.js +++ b/ui/app/pages/settings/settings.component.js @@ -4,6 +4,7 @@ import { Switch, Route, matchPath, withRouter } from 'react-router-dom' import TabBar from '../../components/app/tab-bar' import c from 'classnames' import SettingsTab from './settings-tab' +import ConnectionsTab from './connections-tab' import NetworksTab from './networks-tab' import AdvancedTab from './advanced-tab' import InfoTab from './info-tab' @@ -14,6 +15,7 @@ import { ADVANCED_ROUTE, SECURITY_ROUTE, GENERAL_ROUTE, + CONNECTIONS_ROUTE, ABOUT_US_ROUTE, SETTINGS_ROUTE, NETWORKS_ROUTE, @@ -148,6 +150,7 @@ class SettingsPage extends PureComponent { <TabBar tabs={[ { content: t('general'), description: t('generalSettingsDescription'), key: GENERAL_ROUTE }, + { content: t('connections'), description: t('connectionsSettingsDescription'), key: CONNECTIONS_ROUTE }, { content: t('advanced'), description: t('advancedSettingsDescription'), key: ADVANCED_ROUTE }, { content: t('contacts'), description: t('contactsSettingsDescription'), key: CONTACT_LIST_ROUTE }, { content: t('securityAndPrivacy'), description: t('securitySettingsDescription'), key: SECURITY_ROUTE }, @@ -175,6 +178,11 @@ class SettingsPage extends PureComponent { /> <Route exact + path={CONNECTIONS_ROUTE} + component={ConnectionsTab} + /> + <Route + exact path={ABOUT_US_ROUTE} component={InfoTab} /> diff --git a/ui/app/pages/settings/settings.container.js b/ui/app/pages/settings/settings.container.js index 25c6c0994..8fe6614df 100644 --- a/ui/app/pages/settings/settings.container.js +++ b/ui/app/pages/settings/settings.container.js @@ -8,6 +8,7 @@ import { ENVIRONMENT_TYPE_POPUP } from '../../../../app/scripts/lib/enums' import { getEnvironmentType } from '../../../../app/scripts/lib/util' import { + CONNECTIONS_ROUTE, ADVANCED_ROUTE, SECURITY_ROUTE, GENERAL_ROUTE, @@ -24,6 +25,7 @@ import { const ROUTES_TO_I18N_KEYS = { [GENERAL_ROUTE]: 'general', + [CONNECTIONS_ROUTE]: 'connections', [ADVANCED_ROUTE]: 'advanced', [SECURITY_ROUTE]: 'securityAndPrivacy', [ABOUT_US_ROUTE]: 'about', diff --git a/ui/app/store/actions.js b/ui/app/store/actions.js index c4fea1fd8..cbf8e6283 100644 --- a/ui/app/store/actions.js +++ b/ui/app/store/actions.js @@ -349,7 +349,6 @@ var actions = { approveProviderRequestByOrigin, rejectProviderRequestByOrigin, - forceApproveProviderRequestByOrigin, clearApprovedOrigins, setFirstTimeFlowType, @@ -2650,12 +2649,6 @@ function approveProviderRequestByOrigin (origin) { } } -function forceApproveProviderRequestByOrigin (origin) { - return () => { - background.forceApproveProviderRequestByOrigin(origin) - } -} - function rejectProviderRequestByOrigin (origin) { return () => { background.rejectProviderRequestByOrigin(origin) |