aboutsummaryrefslogtreecommitdiffstats
path: root/ui/app/components
diff options
context:
space:
mode:
Diffstat (limited to 'ui/app/components')
-rw-r--r--ui/app/components/index.scss2
-rw-r--r--ui/app/components/modals/clear-approved-origins/clear-approved-origins.component.js39
-rw-r--r--ui/app/components/modals/clear-approved-origins/clear-approved-origins.container.js16
-rw-r--r--ui/app/components/modals/clear-approved-origins/index.js1
-rw-r--r--ui/app/components/modals/modal.js14
-rw-r--r--ui/app/components/network-display/index.scss5
-rw-r--r--ui/app/components/network-display/network-display.component.js17
-rw-r--r--ui/app/components/page-container/index.scss1
-rw-r--r--ui/app/components/pages/home/home.component.js10
-rw-r--r--ui/app/components/pages/home/home.container.js2
-rw-r--r--ui/app/components/pages/provider-approval/index.js1
-rw-r--r--ui/app/components/pages/provider-approval/provider-approval.component.js28
-rw-r--r--ui/app/components/pages/provider-approval/provider-approval.container.js12
-rw-r--r--ui/app/components/pages/settings/settings-tab/settings-tab.component.js63
-rw-r--r--ui/app/components/pages/settings/settings-tab/settings-tab.container.js8
-rw-r--r--ui/app/components/provider-page-container/index.js3
-rw-r--r--ui/app/components/provider-page-container/index.scss121
-rw-r--r--ui/app/components/provider-page-container/provider-page-container-content/index.js1
-rw-r--r--ui/app/components/provider-page-container/provider-page-container-content/provider-page-container-content.component.js77
-rw-r--r--ui/app/components/provider-page-container/provider-page-container-content/provider-page-container-content.container.js11
-rw-r--r--ui/app/components/provider-page-container/provider-page-container-header/index.js1
-rw-r--r--ui/app/components/provider-page-container/provider-page-container-header/provider-page-container-header.component.js12
-rw-r--r--ui/app/components/provider-page-container/provider-page-container.component.js50
23 files changed, 487 insertions, 8 deletions
diff --git a/ui/app/components/index.scss b/ui/app/components/index.scss
index 72de6cb93..e27b0f182 100644
--- a/ui/app/components/index.scss
+++ b/ui/app/components/index.scss
@@ -32,6 +32,8 @@
@import './pages/index';
+@import './provider-page-container/index';
+
@import './selected-account/index';
@import './sender-to-recipient/index';
diff --git a/ui/app/components/modals/clear-approved-origins/clear-approved-origins.component.js b/ui/app/components/modals/clear-approved-origins/clear-approved-origins.component.js
new file mode 100644
index 000000000..ceaa20a95
--- /dev/null
+++ b/ui/app/components/modals/clear-approved-origins/clear-approved-origins.component.js
@@ -0,0 +1,39 @@
+import React, { PureComponent } from 'react'
+import PropTypes from 'prop-types'
+import Modal, { ModalContent } from '../../modal'
+
+export default class ClearApprovedOrigins extends PureComponent {
+ static propTypes = {
+ hideModal: PropTypes.func.isRequired,
+ clearApprovedOrigins: PropTypes.func.isRequired,
+ }
+
+ static contextTypes = {
+ t: PropTypes.func,
+ }
+
+ handleClear = () => {
+ const { clearApprovedOrigins, hideModal } = this.props
+ clearApprovedOrigins()
+ hideModal()
+ }
+
+ render () {
+ const { t } = this.context
+
+ return (
+ <Modal
+ onSubmit={this.handleClear}
+ onCancel={() => this.props.hideModal()}
+ submitText={t('ok')}
+ cancelText={t('nevermind')}
+ submitType="secondary"
+ >
+ <ModalContent
+ title={t('clearApprovalData')}
+ description={t('confirmClear')}
+ />
+ </Modal>
+ )
+ }
+}
diff --git a/ui/app/components/modals/clear-approved-origins/clear-approved-origins.container.js b/ui/app/components/modals/clear-approved-origins/clear-approved-origins.container.js
new file mode 100644
index 000000000..3a801a062
--- /dev/null
+++ b/ui/app/components/modals/clear-approved-origins/clear-approved-origins.container.js
@@ -0,0 +1,16 @@
+import { connect } from 'react-redux'
+import { compose } from 'recompose'
+import withModalProps from '../../../higher-order-components/with-modal-props'
+import ClearApprovedOriginsComponent from './clear-approved-origins.component'
+import { clearApprovedOrigins } from '../../../actions'
+
+const mapDispatchToProps = dispatch => {
+ return {
+ clearApprovedOrigins: () => dispatch(clearApprovedOrigins()),
+ }
+}
+
+export default compose(
+ withModalProps,
+ connect(null, mapDispatchToProps)
+)(ClearApprovedOriginsComponent)
diff --git a/ui/app/components/modals/clear-approved-origins/index.js b/ui/app/components/modals/clear-approved-origins/index.js
new file mode 100644
index 000000000..b3e321995
--- /dev/null
+++ b/ui/app/components/modals/clear-approved-origins/index.js
@@ -0,0 +1 @@
+export { default } from './clear-approved-origins.container'
diff --git a/ui/app/components/modals/modal.js b/ui/app/components/modals/modal.js
index 338229a28..5aff4f5e1 100644
--- a/ui/app/components/modals/modal.js
+++ b/ui/app/components/modals/modal.js
@@ -28,6 +28,7 @@ import ConfirmCustomizeGasModal from './customize-gas'
import CancelTransaction from './cancel-transaction'
import WelcomeBeta from './welcome-beta'
import RejectTransactions from './reject-transactions'
+import ClearApprovedOrigins from './clear-approved-origins'
const modalContainerBaseStyle = {
transform: 'translate3d(-50%, 0, 0px)',
@@ -212,6 +213,19 @@ const MODALS = {
},
},
+ CLEAR_APPROVED_ORIGINS: {
+ contents: h(ClearApprovedOrigins),
+ mobileModalStyle: {
+ ...modalContainerMobileStyle,
+ },
+ laptopModalStyle: {
+ ...modalContainerLaptopStyle,
+ },
+ contentStyle: {
+ borderRadius: '8px',
+ },
+ },
+
OLD_UI_NOTIFICATION_MODAL: {
contents: [
h(NotifcationModal, {
diff --git a/ui/app/components/network-display/index.scss b/ui/app/components/network-display/index.scss
index 2085cff67..e9f2f2057 100644
--- a/ui/app/components/network-display/index.scss
+++ b/ui/app/components/network-display/index.scss
@@ -3,11 +3,14 @@
display: flex;
align-items: center;
justify-content: flex-start;
- background-color: lighten(rgb(125, 128, 130), 45%);
padding: 0 10px;
border-radius: 4px;
height: 25px;
+ &--colored {
+ background-color: lighten(rgb(125, 128, 130), 45%);
+ }
+
&--mainnet {
background-color: lighten($blue-lagoon, 68%);
}
diff --git a/ui/app/components/network-display/network-display.component.js b/ui/app/components/network-display/network-display.component.js
index 82f9ff9c3..22d617099 100644
--- a/ui/app/components/network-display/network-display.component.js
+++ b/ui/app/components/network-display/network-display.component.js
@@ -16,7 +16,12 @@ const networkToClassHash = {
}
export default class NetworkDisplay extends Component {
+ static defaultProps = {
+ colored: true,
+ }
+
static propTypes = {
+ colored: PropTypes.bool,
network: PropTypes.string,
provider: PropTypes.object,
}
@@ -41,14 +46,16 @@ export default class NetworkDisplay extends Component {
}
render () {
- const { network, provider: { type, nickname } } = this.props
+ const { colored, network, provider: { type, nickname } } = this.props
const networkClass = networkToClassHash[network]
return (
- <div className={classnames(
- 'network-display__container',
- networkClass && ('network-display__container--' + networkClass)
- )}>
+ <div
+ className={classnames('network-display__container', {
+ 'network-display__container--colored': colored,
+ ['network-display__container--' + networkClass]: colored && networkClass,
+ })}
+ >
{
networkClass
? <div className={`network-display__icon network-display__icon--${networkClass}`} />
diff --git a/ui/app/components/page-container/index.scss b/ui/app/components/page-container/index.scss
index 6742e3082..ba1215e84 100644
--- a/ui/app/components/page-container/index.scss
+++ b/ui/app/components/page-container/index.scss
@@ -101,6 +101,7 @@
font-size: 2rem;
font-weight: 500;
line-height: 2rem;
+ margin-right: 1.5rem;
}
&__subtitle {
diff --git a/ui/app/components/pages/home/home.component.js b/ui/app/components/pages/home/home.component.js
index d3c71c4f6..b9ec3c258 100644
--- a/ui/app/components/pages/home/home.component.js
+++ b/ui/app/components/pages/home/home.component.js
@@ -4,6 +4,8 @@ import Media from 'react-media'
import { Redirect } from 'react-router-dom'
import WalletView from '../../wallet-view'
import TransactionView from '../../transaction-view'
+import ProviderApproval from '../provider-approval'
+
import {
INITIALIZE_BACKUP_PHRASE_ROUTE,
RESTORE_VAULT_ROUTE,
@@ -21,6 +23,7 @@ export default class Home extends PureComponent {
seedWords: PropTypes.string,
suggestedTokens: PropTypes.object,
unconfirmedTransactionsCount: PropTypes.number,
+ providerRequests: PropTypes.array,
}
componentDidMount () {
@@ -46,6 +49,7 @@ export default class Home extends PureComponent {
lostAccounts,
forgottenPassword,
seedWords,
+ providerRequests,
} = this.props
// notices
@@ -62,6 +66,12 @@ export default class Home extends PureComponent {
return <Redirect to={{ pathname: RESTORE_VAULT_ROUTE }} />
}
+ if (providerRequests && providerRequests.length > 0) {
+ return (
+ <ProviderApproval providerRequest={providerRequests[0]} />
+ )
+ }
+
return (
<div className="main-container">
<div className="account-and-transaction-details">
diff --git a/ui/app/components/pages/home/home.container.js b/ui/app/components/pages/home/home.container.js
index 58001df6b..bb8cf5e81 100644
--- a/ui/app/components/pages/home/home.container.js
+++ b/ui/app/components/pages/home/home.container.js
@@ -11,6 +11,7 @@ const mapStateToProps = state => {
lostAccounts,
seedWords,
suggestedTokens,
+ providerRequests,
} = metamask
const { forgottenPassword } = appState
@@ -21,6 +22,7 @@ const mapStateToProps = state => {
seedWords,
suggestedTokens,
unconfirmedTransactionsCount: unconfirmedTransactionsCountSelector(state),
+ providerRequests,
}
}
diff --git a/ui/app/components/pages/provider-approval/index.js b/ui/app/components/pages/provider-approval/index.js
new file mode 100644
index 000000000..4162f3155
--- /dev/null
+++ b/ui/app/components/pages/provider-approval/index.js
@@ -0,0 +1 @@
+export { default } from './provider-approval.container'
diff --git a/ui/app/components/pages/provider-approval/provider-approval.component.js b/ui/app/components/pages/provider-approval/provider-approval.component.js
new file mode 100644
index 000000000..da98bc3fc
--- /dev/null
+++ b/ui/app/components/pages/provider-approval/provider-approval.component.js
@@ -0,0 +1,28 @@
+import PropTypes from 'prop-types'
+import React, { Component } from 'react'
+import ProviderPageContainer from '../../provider-page-container'
+
+export default class ProviderApproval extends Component {
+ static propTypes = {
+ approveProviderRequest: PropTypes.func.isRequired,
+ providerRequest: PropTypes.object.isRequired,
+ rejectProviderRequest: PropTypes.func.isRequired,
+ };
+
+ static contextTypes = {
+ t: PropTypes.func,
+ };
+
+ render () {
+ const { approveProviderRequest, providerRequest, rejectProviderRequest } = this.props
+ return (
+ <ProviderPageContainer
+ approveProviderRequest={approveProviderRequest}
+ origin={providerRequest.origin}
+ rejectProviderRequest={rejectProviderRequest}
+ siteImage={providerRequest.siteImage}
+ siteTitle={providerRequest.siteTitle}
+ />
+ )
+ }
+}
diff --git a/ui/app/components/pages/provider-approval/provider-approval.container.js b/ui/app/components/pages/provider-approval/provider-approval.container.js
new file mode 100644
index 000000000..b223244a1
--- /dev/null
+++ b/ui/app/components/pages/provider-approval/provider-approval.container.js
@@ -0,0 +1,12 @@
+import { connect } from 'react-redux'
+import ProviderApproval from './provider-approval.component'
+import { approveProviderRequest, rejectProviderRequest } from '../../../actions'
+
+function mapDispatchToProps (dispatch) {
+ return {
+ approveProviderRequest: origin => dispatch(approveProviderRequest(origin)),
+ rejectProviderRequest: origin => dispatch(rejectProviderRequest(origin)),
+ }
+}
+
+export default connect(null, mapDispatchToProps)(ProviderApproval)
diff --git a/ui/app/components/pages/settings/settings-tab/settings-tab.component.js b/ui/app/components/pages/settings/settings-tab/settings-tab.component.js
index cd81491a8..a0a8ed47e 100644
--- a/ui/app/components/pages/settings/settings-tab/settings-tab.component.js
+++ b/ui/app/components/pages/settings/settings-tab/settings-tab.component.js
@@ -39,12 +39,15 @@ export default class SettingsTab extends PureComponent {
metamask: PropTypes.object,
setUseBlockie: PropTypes.func,
setHexDataFeatureFlag: PropTypes.func,
+ setPrivacyMode: PropTypes.func,
+ privacyMode: PropTypes.bool,
setCurrentCurrency: PropTypes.func,
setRpcTarget: PropTypes.func,
delRpcTarget: PropTypes.func,
displayWarning: PropTypes.func,
revealSeedConfirmation: PropTypes.func,
setFeatureFlagToBeta: PropTypes.func,
+ showClearApprovalModal: PropTypes.func,
showResetAccountConfirmationModal: PropTypes.func,
warning: PropTypes.string,
history: PropTypes.object,
@@ -276,6 +279,36 @@ export default class SettingsTab extends PureComponent {
)
}
+ 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="secondary"
+ 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
@@ -461,6 +494,32 @@ export default class SettingsTab 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)}
+ activeLabel=""
+ inactiveLabel=""
+ />
+ </div>
+ </div>
+ </div>
+ )
+ }
+
render () {
const { warning, isMascara } = this.props
@@ -475,8 +534,10 @@ export default class SettingsTab extends PureComponent {
{ this.renderSeedWords() }
{ !isMascara && this.renderOldUI() }
{ this.renderResetAccount() }
- { this.renderBlockieOptIn() }
+ { this.renderClearApproval() }
+ { this.renderPrivacyOptIn() }
{ this.renderHexDataOptIn() }
+ { this.renderBlockieOptIn() }
</div>
)
}
diff --git a/ui/app/components/pages/settings/settings-tab/settings-tab.container.js b/ui/app/components/pages/settings/settings-tab/settings-tab.container.js
index f4110207e..b6c33a5b2 100644
--- a/ui/app/components/pages/settings/settings-tab/settings-tab.container.js
+++ b/ui/app/components/pages/settings/settings-tab/settings-tab.container.js
@@ -22,7 +22,10 @@ const mapStateToProps = state => {
conversionDate,
nativeCurrency,
useBlockie,
- featureFlags: { sendHexData } = {},
+ featureFlags: {
+ sendHexData,
+ privacyMode,
+ } = {},
provider = {},
isMascara,
currentLocale,
@@ -38,6 +41,7 @@ const mapStateToProps = state => {
nativeCurrency,
useBlockie,
sendHexData,
+ privacyMode,
provider,
useNativeCurrencyAsPrimaryCurrency,
}
@@ -55,10 +59,12 @@ const mapDispatchToProps = dispatch => {
return dispatch(setFeatureFlag('betaUI', false, 'OLD_UI_NOTIFICATION_MODAL'))
},
setHexDataFeatureFlag: shouldShow => dispatch(setFeatureFlag('sendHexData', shouldShow)),
+ setPrivacyMode: enabled => dispatch(setFeatureFlag('privacyMode', enabled)),
showResetAccountConfirmationModal: () => dispatch(showModal({ name: 'CONFIRM_RESET_ACCOUNT' })),
setUseNativeCurrencyAsPrimaryCurrencyPreference: value => {
return dispatch(setUseNativeCurrencyAsPrimaryCurrencyPreference(value))
},
+ showClearApprovalModal: () => dispatch(showModal({ name: 'CLEAR_APPROVED_ORIGINS' })),
}
}
diff --git a/ui/app/components/provider-page-container/index.js b/ui/app/components/provider-page-container/index.js
new file mode 100644
index 000000000..927c35940
--- /dev/null
+++ b/ui/app/components/provider-page-container/index.js
@@ -0,0 +1,3 @@
+export {default} from './provider-page-container.component'
+export {default as ProviderPageContainerContent} from './provider-page-container-content'
+export {default as ProviderPageContainerHeader} from './provider-page-container-header'
diff --git a/ui/app/components/provider-page-container/index.scss b/ui/app/components/provider-page-container/index.scss
new file mode 100644
index 000000000..8d35ac179
--- /dev/null
+++ b/ui/app/components/provider-page-container/index.scss
@@ -0,0 +1,121 @@
+.provider-approval-container {
+ display: flex;
+
+ &__header {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+ border-bottom: 1px solid $geyser;
+ padding: 9px;
+ }
+
+ &__content {
+ display: flex;
+ overflow-y: auto;
+ flex: 1;
+ flex-direction: column;
+ justify-content: space-between;
+ color: #7C808E;
+
+ h1, h2 {
+ color: #4A4A4A;
+ display: flex;
+ justify-content: center;
+ text-align: center;
+ }
+
+ h2 {
+ font-size: 16px;
+ line-height: 18px;
+ padding: 20px;
+ }
+
+ h1 {
+ font-size: 22px;
+ line-height: 26px;
+ padding: 20px;
+ }
+
+ p {
+ padding: 0 40px;
+ text-align: center;
+ font-size: 12px;
+ line-height: 18px;
+ }
+
+ a, a:hover {
+ color: $dodger-blue;
+ }
+
+ .provider-approval-visual {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-evenly;
+ position: relative;
+ margin: 0 32px;
+
+ section {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ flex: 1;
+ }
+
+ h1 {
+ font-size: 14px;
+ line-height: 18px;
+ padding: 8px 0 0;
+ }
+
+ h2 {
+ font-size: 10px;
+ line-height: 14px;
+ padding: 0;
+ color: #A2A4AC;
+ }
+
+ &__check {
+ width: 40px;
+ height: 40px;
+ background: white url("/images/provider-approval-check.svg") no-repeat;
+ margin-top: 14px;
+ }
+
+ &__identicon {
+ width: 64px;
+ height: 64px;
+
+ &--default {
+ background-color: #777A87;
+ color: white;
+ width: 64px;
+ height: 64px;
+ border-radius: 32px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-weight: bold;
+ }
+ }
+
+ &:before {
+ border-top: 2px dashed #CDD1E4;
+ content: "";
+ margin: 0 auto;
+ position: absolute;
+ top: 32px;
+ left: 0;
+ bottom: 0;
+ right: 0;
+ width: 65%;
+ z-index: -1;
+ }
+ }
+
+ .secure-badge {
+ display: flex;
+ justify-content: center;
+ padding: 25px;
+ }
+ }
+}
diff --git a/ui/app/components/provider-page-container/provider-page-container-content/index.js b/ui/app/components/provider-page-container/provider-page-container-content/index.js
new file mode 100644
index 000000000..73e491adc
--- /dev/null
+++ b/ui/app/components/provider-page-container/provider-page-container-content/index.js
@@ -0,0 +1 @@
+export {default} from './provider-page-container-content.container'
diff --git a/ui/app/components/provider-page-container/provider-page-container-content/provider-page-container-content.component.js b/ui/app/components/provider-page-container/provider-page-container-content/provider-page-container-content.component.js
new file mode 100644
index 000000000..268db613f
--- /dev/null
+++ b/ui/app/components/provider-page-container/provider-page-container-content/provider-page-container-content.component.js
@@ -0,0 +1,77 @@
+import PropTypes from 'prop-types'
+import React, {PureComponent} from 'react'
+import Identicon from '../../identicon'
+
+export default class ProviderPageContainerContent extends PureComponent {
+ static propTypes = {
+ origin: PropTypes.string.isRequired,
+ selectedIdentity: PropTypes.string.isRequired,
+ siteImage: PropTypes.string,
+ siteTitle: PropTypes.string.isRequired,
+ }
+
+ static contextTypes = {
+ t: PropTypes.func,
+ };
+
+ renderConnectVisual = () => {
+ const { origin, selectedIdentity, siteImage, siteTitle } = this.props
+
+ return (
+ <div className="provider-approval-visual">
+ <section>
+ {siteImage ? (
+ <img
+ className="provider-approval-visual__identicon"
+ src={siteImage}
+ />
+ ) : (
+ <i className="provider-approval-visual__identicon--default">
+ {siteTitle.charAt(0).toUpperCase()}
+ </i>
+ )}
+ <h1>{siteTitle}</h1>
+ <h2>{origin}</h2>
+ </section>
+ <span className="provider-approval-visual__check" />
+ <section>
+ <Identicon
+ className="provider-approval-visual__identicon"
+ address={selectedIdentity.address}
+ diameter={64}
+ />
+ <h1>{selectedIdentity.name}</h1>
+ </section>
+ </div>
+ )
+ }
+
+ render () {
+ const { siteTitle } = this.props
+ const { t } = this.context
+
+ return (
+ <div className="provider-approval-container__content">
+ <section>
+ <h2>{t('connectRequest')}</h2>
+ {this.renderConnectVisual()}
+ <h1>{t('providerRequest', [siteTitle])}</h1>
+ <p>
+ {t('providerRequestInfo')}
+ <br/>
+ <a
+ href="https://medium.com/metamask/introducing-privacy-mode-42549d4870fa"
+ target="_blank"
+ rel="noopener noreferrer"
+ >
+ {t('learnMore')}.
+ </a>
+ </p>
+ </section>
+ <section className="secure-badge">
+ <img src="/images/mm-secure.svg" />
+ </section>
+ </div>
+ )
+ }
+}
diff --git a/ui/app/components/provider-page-container/provider-page-container-content/provider-page-container-content.container.js b/ui/app/components/provider-page-container/provider-page-container-content/provider-page-container-content.container.js
new file mode 100644
index 000000000..3ea1ce20e
--- /dev/null
+++ b/ui/app/components/provider-page-container/provider-page-container-content/provider-page-container-content.container.js
@@ -0,0 +1,11 @@
+import { connect } from 'react-redux'
+import ProviderPageContainerContent from './provider-page-container-content.component'
+import { getSelectedIdentity } from '../../../selectors'
+
+const mapStateToProps = (state) => {
+ return {
+ selectedIdentity: getSelectedIdentity(state),
+ }
+}
+
+export default connect(mapStateToProps)(ProviderPageContainerContent)
diff --git a/ui/app/components/provider-page-container/provider-page-container-header/index.js b/ui/app/components/provider-page-container/provider-page-container-header/index.js
new file mode 100644
index 000000000..430627d3a
--- /dev/null
+++ b/ui/app/components/provider-page-container/provider-page-container-header/index.js
@@ -0,0 +1 @@
+export {default} from './provider-page-container-header.component'
diff --git a/ui/app/components/provider-page-container/provider-page-container-header/provider-page-container-header.component.js b/ui/app/components/provider-page-container/provider-page-container-header/provider-page-container-header.component.js
new file mode 100644
index 000000000..41bf6c3dd
--- /dev/null
+++ b/ui/app/components/provider-page-container/provider-page-container-header/provider-page-container-header.component.js
@@ -0,0 +1,12 @@
+import React, {PureComponent} from 'react'
+import NetworkDisplay from '../../network-display'
+
+export default class ProviderPageContainerHeader extends PureComponent {
+ render () {
+ return (
+ <div className="provider-approval-container__header">
+ <NetworkDisplay colored={false} />
+ </div>
+ )
+ }
+}
diff --git a/ui/app/components/provider-page-container/provider-page-container.component.js b/ui/app/components/provider-page-container/provider-page-container.component.js
new file mode 100644
index 000000000..902733616
--- /dev/null
+++ b/ui/app/components/provider-page-container/provider-page-container.component.js
@@ -0,0 +1,50 @@
+import PropTypes from 'prop-types'
+import React, {PureComponent} from 'react'
+import { ProviderPageContainerContent, ProviderPageContainerHeader } from './'
+import { PageContainerFooter } from '../page-container'
+
+export default class ProviderPageContainer extends PureComponent {
+ static propTypes = {
+ approveProviderRequest: PropTypes.func.isRequired,
+ origin: PropTypes.string.isRequired,
+ rejectProviderRequest: PropTypes.func.isRequired,
+ siteImage: PropTypes.string,
+ siteTitle: PropTypes.string.isRequired,
+ };
+
+ static contextTypes = {
+ t: PropTypes.func,
+ };
+
+ onCancel = () => {
+ const { origin, rejectProviderRequest } = this.props
+ rejectProviderRequest(origin)
+ }
+
+ onSubmit = () => {
+ const { approveProviderRequest, origin } = this.props
+ approveProviderRequest(origin)
+ }
+
+ render () {
+ const {origin, siteImage, siteTitle} = this.props
+
+ return (
+ <div className="page-container provider-approval-container">
+ <ProviderPageContainerHeader />
+ <ProviderPageContainerContent
+ origin={origin}
+ siteImage={siteImage}
+ siteTitle={siteTitle}
+ />
+ <PageContainerFooter
+ onCancel={() => this.onCancel()}
+ cancelText={this.context.t('cancel')}
+ onSubmit={() => this.onSubmit()}
+ submitText={this.context.t('connect')}
+ submitButtonType="confirm"
+ />
+ </div>
+ )
+ }
+}