mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-10-22 11:22:43 +02:00
Choose accounts refactor (#13039)
* added wrapper around account list to prevent storybook from collapsing the list * updated translation files * added snap-connect page * refactored account list out of the choose account component * fixed width * removed unnecessary scss from choose-account component, fixed props in choose account story * removed snaps-connect page, added comments to ChooseAccount * updated choose account subtitle text, updated styling for title & subtitle, removed redundant account list story * updated component name, updated paths * fixed linter errors * added comments * removed unused message * removed selectAccounts key from all locales * updated class name for account list header, updated allAreSelected function to use length checks * Revert "removed unused message" This reverts commit 32771bc83c08f120825ef75f0741f3034e7dbecb. * Revert "removed selectAccounts key from all locales" This reverts commit ccfa4a860f9a75693d893d7c404384e719de297e. * updated locale messages to use selectAccounts key * removed stray import * updated scss * updated translation key * removed chooseAccounts key from en locale * removed optional chaining * changes * updated subjectMetadata * updated subject types * update useOriginMetadata function to include unknown subject type * updated permission connect header props, removed host and added subjectType to targetSubjectMetadata * added subjectType to targetSubjectMetadata * removed console.log * changed prop name to iconUrl
This commit is contained in:
parent
8cc185a8b1
commit
f946c030b5
@ -2443,7 +2443,7 @@
|
||||
"message": "Select a higher gas fee to accelerate the processing of your transaction.*"
|
||||
},
|
||||
"selectAccounts": {
|
||||
"message": "Select account(s)"
|
||||
"message": "Select the account(s) to use on this site"
|
||||
},
|
||||
"selectAll": {
|
||||
"message": "Select all"
|
||||
|
@ -38,7 +38,7 @@ function sendMetadataHandler(
|
||||
end,
|
||||
{ addSubjectMetadata, subjectType },
|
||||
) {
|
||||
const { params } = req;
|
||||
const { origin, params } = req;
|
||||
if (params && typeof params === 'object' && !Array.isArray(params)) {
|
||||
const { icon = null, name = null, ...remainingParams } = params;
|
||||
|
||||
@ -47,6 +47,7 @@ function sendMetadataHandler(
|
||||
iconUrl: icon,
|
||||
name,
|
||||
subjectType,
|
||||
origin,
|
||||
});
|
||||
} else {
|
||||
return end(ethErrors.rpc.invalidParams({ data: params }));
|
||||
|
@ -2725,7 +2725,6 @@ export default class MetamaskController extends EventEmitter {
|
||||
// Miscellaneous
|
||||
addSubjectMetadata: this.subjectMetadataController.addSubjectMetadata.bind(
|
||||
this.subjectMetadataController,
|
||||
origin,
|
||||
),
|
||||
getProviderState: this.getProviderState.bind(this),
|
||||
getUnlockPromise: this.appStateController.getUnlockPromise.bind(
|
||||
|
@ -50,9 +50,10 @@ export const MESSAGE_TYPE = {
|
||||
* third parties and itself (e.g. when the background communicated with the UI).
|
||||
*/
|
||||
export const SUBJECT_TYPES = {
|
||||
WEBSITE: 'website',
|
||||
EXTENSION: 'extension',
|
||||
INTERNAL: 'internal',
|
||||
UNKNOWN: 'unknown',
|
||||
WEBSITE: 'website',
|
||||
};
|
||||
|
||||
export const POLLING_TOKEN_ENVIRONMENT_TYPES = {
|
||||
|
@ -100,7 +100,7 @@ export default class PermissionPageContainerContent extends PureComponent {
|
||||
<div className="permission-approval-container__content">
|
||||
<div className="permission-approval-container__content-container">
|
||||
<PermissionsConnectHeader
|
||||
icon={subjectMetadata.iconUrl}
|
||||
iconUrl={subjectMetadata.iconUrl}
|
||||
iconName={subjectMetadata.name}
|
||||
headerTitle={title}
|
||||
headerText={
|
||||
|
@ -17,13 +17,14 @@
|
||||
text-align: center;
|
||||
color: $Black-100;
|
||||
margin-top: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&__subtitle {
|
||||
@include H6;
|
||||
|
||||
text-align: center;
|
||||
color: $Grey-500;
|
||||
color: $Black-100;
|
||||
}
|
||||
|
||||
&__subtitle {
|
||||
|
@ -4,7 +4,7 @@ import SiteOrigin from '../../ui/site-origin/site-origin';
|
||||
|
||||
export default class PermissionsConnectHeader extends Component {
|
||||
static propTypes = {
|
||||
icon: PropTypes.string,
|
||||
iconUrl: PropTypes.string,
|
||||
iconName: PropTypes.string.isRequired,
|
||||
siteOrigin: PropTypes.string.isRequired,
|
||||
headerTitle: PropTypes.node,
|
||||
@ -12,17 +12,17 @@ export default class PermissionsConnectHeader extends Component {
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
icon: null,
|
||||
iconUrl: null,
|
||||
headerTitle: '',
|
||||
headerText: '',
|
||||
};
|
||||
|
||||
renderHeaderIcon() {
|
||||
const { icon, iconName, siteOrigin } = this.props;
|
||||
const { iconUrl, iconName, siteOrigin } = this.props;
|
||||
|
||||
return (
|
||||
<div className="permissions-connect-header__icon">
|
||||
<SiteOrigin siteOrigin={siteOrigin} iconSrc={icon} name={iconName} />
|
||||
<SiteOrigin siteOrigin={siteOrigin} iconSrc={iconUrl} name={iconName} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
175
ui/components/ui/account-list/account-list.js
Normal file
175
ui/components/ui/account-list/account-list.js
Normal file
@ -0,0 +1,175 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import { useI18nContext } from '../../../hooks/useI18nContext';
|
||||
import CheckBox, { CHECKED, INDETERMINATE, UNCHECKED } from '../check-box';
|
||||
import Identicon from '../identicon';
|
||||
import UserPreferencedCurrencyDisplay from '../../app/user-preferenced-currency-display';
|
||||
import { PRIMARY } from '../../../helpers/constants/common';
|
||||
import Tooltip from '../tooltip';
|
||||
|
||||
const AccountList = ({
|
||||
selectNewAccountViaModal,
|
||||
accounts,
|
||||
addressLastConnectedMap,
|
||||
selectedAccounts,
|
||||
nativeCurrency,
|
||||
allAreSelected,
|
||||
deselectAll,
|
||||
selectAll,
|
||||
handleAccountClick,
|
||||
}) => {
|
||||
const t = useI18nContext();
|
||||
|
||||
const Header = () => {
|
||||
let checked;
|
||||
if (allAreSelected()) {
|
||||
checked = CHECKED;
|
||||
} else if (selectedAccounts.size === 0) {
|
||||
checked = UNCHECKED;
|
||||
} else {
|
||||
checked = INDETERMINATE;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames({
|
||||
'choose-account-list__header--one-item': accounts.length === 1,
|
||||
'choose-account-list__header--multiple-items': accounts.length > 1,
|
||||
})}
|
||||
>
|
||||
{accounts.length > 1 ? (
|
||||
<div className="choose-account-list__select-all">
|
||||
<CheckBox
|
||||
className="choose-account-list__header-check-box"
|
||||
checked={checked}
|
||||
onClick={() => (allAreSelected() ? deselectAll() : selectAll())}
|
||||
/>
|
||||
<div className="choose-account-list__text-grey">
|
||||
{t('selectAll')}
|
||||
</div>
|
||||
<Tooltip
|
||||
position="bottom"
|
||||
html={
|
||||
<div style={{ width: 200, padding: 4 }}>
|
||||
{t('selectingAllWillAllow')}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<i className="fa fa-info-circle" />
|
||||
</Tooltip>
|
||||
</div>
|
||||
) : null}
|
||||
<div
|
||||
className="choose-account-list__text-blue"
|
||||
onClick={() => selectNewAccountViaModal(handleAccountClick)}
|
||||
>
|
||||
{t('newAccount')}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const List = () => {
|
||||
return (
|
||||
<div className="choose-account-list__wrapper">
|
||||
<div className="choose-account-list__list">
|
||||
{accounts.map((account, index) => {
|
||||
const { address, addressLabel, balance } = account;
|
||||
return (
|
||||
<div
|
||||
key={`choose-account-list-${index}`}
|
||||
onClick={() => handleAccountClick(address)}
|
||||
className="choose-account-list__account"
|
||||
>
|
||||
<div className="choose-account-list__account-info-wrapper">
|
||||
<CheckBox
|
||||
className="choose-account-list__list-check-box"
|
||||
checked={selectedAccounts.has(address)}
|
||||
/>
|
||||
<Identicon diameter={34} address={address} />
|
||||
<div className="choose-account-list__account__info">
|
||||
<div className="choose-account-list__account__label">
|
||||
{addressLabel}
|
||||
</div>
|
||||
<UserPreferencedCurrencyDisplay
|
||||
className="choose-account-list__account__balance"
|
||||
type={PRIMARY}
|
||||
value={balance}
|
||||
style={{ color: '#6A737D' }}
|
||||
suffix={nativeCurrency}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{addressLastConnectedMap[address] ? (
|
||||
<Tooltip
|
||||
title={`${t('lastConnected')} ${
|
||||
addressLastConnectedMap[address]
|
||||
}`}
|
||||
>
|
||||
<i className="fa fa-info-circle" />
|
||||
</Tooltip>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="choose-account-list">
|
||||
<Header />
|
||||
<List />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
AccountList.propTypes = {
|
||||
/**
|
||||
* Array of user account objects
|
||||
*/
|
||||
accounts: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
address: PropTypes.string,
|
||||
addressLabel: PropTypes.string,
|
||||
lastConnectedDate: PropTypes.string,
|
||||
balance: PropTypes.string,
|
||||
}),
|
||||
).isRequired,
|
||||
/**
|
||||
* Function to select a new account via modal
|
||||
*/
|
||||
selectNewAccountViaModal: PropTypes.func.isRequired,
|
||||
/**
|
||||
* A map of the last connected addresses
|
||||
*/
|
||||
addressLastConnectedMap: PropTypes.object,
|
||||
/**
|
||||
* Native currency of current chain
|
||||
*/
|
||||
nativeCurrency: PropTypes.string.isRequired,
|
||||
/**
|
||||
* Currently selected accounts
|
||||
*/
|
||||
selectedAccounts: PropTypes.object.isRequired,
|
||||
/**
|
||||
* Function to check if all accounts are selected
|
||||
*/
|
||||
allAreSelected: PropTypes.func.isRequired,
|
||||
/**
|
||||
* Function to deselect all accounts
|
||||
*/
|
||||
deselectAll: PropTypes.func.isRequired,
|
||||
/**
|
||||
* Function to select all accounts
|
||||
*/
|
||||
selectAll: PropTypes.func.isRequired,
|
||||
/**
|
||||
* Function to handle account click
|
||||
*/
|
||||
handleAccountClick: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default AccountList;
|
1
ui/components/ui/account-list/index.js
Normal file
1
ui/components/ui/account-list/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './account-list';
|
140
ui/components/ui/account-list/index.scss
Normal file
140
ui/components/ui/account-list/index.scss
Normal file
@ -0,0 +1,140 @@
|
||||
.choose-account-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
|
||||
&__header--one-item,
|
||||
&__header--multiple-items {
|
||||
display: flex;
|
||||
flex: 0;
|
||||
margin-top: 36px;
|
||||
width: 100%;
|
||||
padding-inline-start: 15px;
|
||||
padding-inline-end: 17px;
|
||||
}
|
||||
|
||||
&__header--one-item {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
&__header--multiple-items {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&__select-all {
|
||||
display: flex;
|
||||
margin-inline-start: 16px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__header-check-box {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
&__wrapper {
|
||||
width: 92%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__list {
|
||||
flex: 2 1 0;
|
||||
width: 92%;
|
||||
max-height: max-content;
|
||||
border: 1px solid #d0d5da;
|
||||
box-sizing: border-box;
|
||||
border-radius: 8px;
|
||||
margin-top: 8px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
&__account {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
border-bottom: 1px solid #d2d8dd;
|
||||
justify-content: space-between;
|
||||
|
||||
&:last-of-type {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $Grey-000;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-inline-start: 16px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
&__label {
|
||||
@include H6;
|
||||
|
||||
color: $Black-100;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__balance {
|
||||
@include H7;
|
||||
|
||||
color: $Grey-500;
|
||||
}
|
||||
|
||||
&__last-connected {
|
||||
@include H8;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
color: $primary-blue;
|
||||
}
|
||||
}
|
||||
|
||||
&__account-info-wrapper {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
&__list-check-box {
|
||||
margin-inline-end: 16px;
|
||||
|
||||
i {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
.fa-info-circle {
|
||||
color: $Grey-200;
|
||||
cursor: pointer;
|
||||
margin-inline-start: 8px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.fa-info-circle:hover {
|
||||
color: $Grey-300;
|
||||
}
|
||||
|
||||
&__text,
|
||||
&__text-blue,
|
||||
&__text-grey {
|
||||
@include H6;
|
||||
}
|
||||
|
||||
&__text-blue {
|
||||
color: $primary-blue;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__text-grey {
|
||||
color: $Grey-500;
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
/** Please import your files in alphabetical order **/
|
||||
@import 'account-mismatch-warning/index';
|
||||
@import 'account-list/index';
|
||||
@import 'actionable-message/index';
|
||||
@import 'alert-circle-icon/index';
|
||||
@import 'alert/index';
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { useSelector } from 'react-redux';
|
||||
import { getSubjectMetadata } from '../selectors';
|
||||
import { SUBJECT_TYPES } from '../../shared/constants/app';
|
||||
|
||||
/**
|
||||
* @typedef {Object} OriginMetadata
|
||||
@ -27,6 +28,7 @@ export function useOriginMetadata(origin) {
|
||||
host: url.host,
|
||||
hostname: url.hostname,
|
||||
origin,
|
||||
subjectType: SUBJECT_TYPES.UNKNOWN,
|
||||
};
|
||||
|
||||
if (subjectMetadata?.[origin]) {
|
||||
|
@ -1,242 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import Identicon from '../../../components/ui/identicon';
|
||||
import Button from '../../../components/ui/button';
|
||||
import CheckBox, {
|
||||
CHECKED,
|
||||
INDETERMINATE,
|
||||
UNCHECKED,
|
||||
} from '../../../components/ui/check-box';
|
||||
import Tooltip from '../../../components/ui/tooltip';
|
||||
import { PRIMARY } from '../../../helpers/constants/common';
|
||||
import UserPreferencedCurrencyDisplay from '../../../components/app/user-preferenced-currency-display';
|
||||
import PermissionsConnectHeader from '../../../components/app/permissions-connect-header';
|
||||
import PermissionsConnectFooter from '../../../components/app/permissions-connect-footer';
|
||||
|
||||
export default class ChooseAccount extends Component {
|
||||
static propTypes = {
|
||||
accounts: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
address: PropTypes.string,
|
||||
addressLabel: PropTypes.string,
|
||||
lastConnectedDate: PropTypes.string,
|
||||
balance: PropTypes.string,
|
||||
}),
|
||||
).isRequired,
|
||||
selectAccounts: PropTypes.func.isRequired,
|
||||
selectNewAccountViaModal: PropTypes.func.isRequired,
|
||||
nativeCurrency: PropTypes.string.isRequired,
|
||||
addressLastConnectedMap: PropTypes.object,
|
||||
cancelPermissionsRequest: PropTypes.func.isRequired,
|
||||
permissionsRequestId: PropTypes.string.isRequired,
|
||||
selectedAccountAddresses: PropTypes.object.isRequired,
|
||||
targetSubjectMetadata: PropTypes.shape({
|
||||
extensionId: PropTypes.string,
|
||||
iconUrl: PropTypes.string,
|
||||
name: PropTypes.string.isRequired,
|
||||
origin: PropTypes.string.isRequired,
|
||||
}),
|
||||
};
|
||||
|
||||
state = {
|
||||
selectedAccounts: this.props.selectedAccountAddresses,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
addressLastConnectedMap: {},
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
t: PropTypes.func,
|
||||
};
|
||||
|
||||
handleAccountClick(address) {
|
||||
const { selectedAccounts } = this.state;
|
||||
|
||||
const newSelectedAccounts = new Set(selectedAccounts);
|
||||
|
||||
if (newSelectedAccounts.has(address)) {
|
||||
newSelectedAccounts.delete(address);
|
||||
} else {
|
||||
newSelectedAccounts.add(address);
|
||||
}
|
||||
|
||||
this.setState({ selectedAccounts: newSelectedAccounts });
|
||||
}
|
||||
|
||||
selectAll() {
|
||||
const { accounts } = this.props;
|
||||
|
||||
const newSelectedAccounts = new Set(
|
||||
accounts.map((account) => account.address),
|
||||
);
|
||||
|
||||
this.setState({ selectedAccounts: newSelectedAccounts });
|
||||
}
|
||||
|
||||
deselectAll() {
|
||||
this.setState({ selectedAccounts: new Set() });
|
||||
}
|
||||
|
||||
allAreSelected() {
|
||||
const { accounts } = this.props;
|
||||
const { selectedAccounts } = this.state;
|
||||
|
||||
return accounts.every(({ address }) => selectedAccounts.has(address));
|
||||
}
|
||||
|
||||
renderAccountsList = () => {
|
||||
const { accounts, nativeCurrency, addressLastConnectedMap } = this.props;
|
||||
const { selectedAccounts } = this.state;
|
||||
return (
|
||||
<div className="permissions-connect-choose-account__accounts-list">
|
||||
{accounts.map((account, index) => {
|
||||
const { address, addressLabel, balance } = account;
|
||||
return (
|
||||
<div
|
||||
key={`permissions-connect-choose-account-${index}`}
|
||||
onClick={() => this.handleAccountClick(address)}
|
||||
className="permissions-connect-choose-account__account"
|
||||
>
|
||||
<div className="permissions-connect-choose-account__account-info-wrapper">
|
||||
<CheckBox
|
||||
className="permissions-connect-choose-account__list-check-box"
|
||||
checked={selectedAccounts.has(address)}
|
||||
/>
|
||||
<Identicon diameter={34} address={address} />
|
||||
<div className="permissions-connect-choose-account__account__info">
|
||||
<div className="permissions-connect-choose-account__account__label">
|
||||
{addressLabel}
|
||||
</div>
|
||||
<UserPreferencedCurrencyDisplay
|
||||
className="permissions-connect-choose-account__account__balance"
|
||||
type={PRIMARY}
|
||||
value={balance}
|
||||
style={{ color: '#6A737D' }}
|
||||
suffix={nativeCurrency}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{addressLastConnectedMap[address] ? (
|
||||
<Tooltip
|
||||
title={`${this.context.t('lastConnected')} ${
|
||||
addressLastConnectedMap[address]
|
||||
}`}
|
||||
>
|
||||
<i className="fa fa-info-circle" />
|
||||
</Tooltip>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
renderAccountsListHeader() {
|
||||
const { t } = this.context;
|
||||
const { selectNewAccountViaModal, accounts } = this.props;
|
||||
const { selectedAccounts } = this.state;
|
||||
|
||||
let checked;
|
||||
if (this.allAreSelected()) {
|
||||
checked = CHECKED;
|
||||
} else if (selectedAccounts.size === 0) {
|
||||
checked = UNCHECKED;
|
||||
} else {
|
||||
checked = INDETERMINATE;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames({
|
||||
'permissions-connect-choose-account__accounts-list-header--one-item':
|
||||
accounts.length === 1,
|
||||
'permissions-connect-choose-account__accounts-list-header--two-items':
|
||||
accounts.length > 1,
|
||||
})}
|
||||
>
|
||||
{accounts.length > 1 ? (
|
||||
<div className="permissions-connect-choose-account__select-all">
|
||||
<CheckBox
|
||||
className="permissions-connect-choose-account__header-check-box"
|
||||
checked={checked}
|
||||
onClick={() =>
|
||||
this.allAreSelected() ? this.deselectAll() : this.selectAll()
|
||||
}
|
||||
/>
|
||||
<div className="permissions-connect-choose-account__text-grey">
|
||||
{this.context.t('selectAll')}
|
||||
</div>
|
||||
<Tooltip
|
||||
position="bottom"
|
||||
html={
|
||||
<div style={{ width: 200, padding: 4 }}>
|
||||
{t('selectingAllWillAllow')}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<i className="fa fa-info-circle" />
|
||||
</Tooltip>
|
||||
</div>
|
||||
) : null}
|
||||
<div
|
||||
className="permissions-connect-choose-account__text-blue"
|
||||
onClick={() =>
|
||||
selectNewAccountViaModal(this.handleAccountClick.bind(this))
|
||||
}
|
||||
>
|
||||
{this.context.t('newAccount')}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
selectAccounts,
|
||||
permissionsRequestId,
|
||||
cancelPermissionsRequest,
|
||||
targetSubjectMetadata,
|
||||
accounts,
|
||||
} = this.props;
|
||||
const { selectedAccounts } = this.state;
|
||||
const { t } = this.context;
|
||||
return (
|
||||
<div className="permissions-connect-choose-account">
|
||||
<PermissionsConnectHeader
|
||||
icon={targetSubjectMetadata.iconUrl}
|
||||
iconName={targetSubjectMetadata.name}
|
||||
headerTitle={t('connectWithMetaMask')}
|
||||
headerText={
|
||||
accounts.length > 0
|
||||
? t('selectAccounts')
|
||||
: t('connectAccountOrCreate')
|
||||
}
|
||||
siteOrigin={targetSubjectMetadata.origin}
|
||||
/>
|
||||
{this.renderAccountsListHeader()}
|
||||
{this.renderAccountsList()}
|
||||
<div className="permissions-connect-choose-account__footer-container">
|
||||
<PermissionsConnectFooter />
|
||||
<div className="permissions-connect-choose-account__bottom-buttons">
|
||||
<Button
|
||||
onClick={() => cancelPermissionsRequest(permissionsRequestId)}
|
||||
type="secondary"
|
||||
>
|
||||
{t('cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => selectAccounts(selectedAccounts)}
|
||||
type="primary"
|
||||
disabled={selectedAccounts.size === 0}
|
||||
>
|
||||
{t('next')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
148
ui/pages/permissions-connect/choose-account/choose-account.js
Normal file
148
ui/pages/permissions-connect/choose-account/choose-account.js
Normal file
@ -0,0 +1,148 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useState } from 'react';
|
||||
import { useI18nContext } from '../../../hooks/useI18nContext';
|
||||
import Button from '../../../components/ui/button';
|
||||
import PermissionsConnectHeader from '../../../components/app/permissions-connect-header';
|
||||
import PermissionsConnectFooter from '../../../components/app/permissions-connect-footer';
|
||||
import AccountList from '../../../components/ui/account-list';
|
||||
|
||||
const ChooseAccount = ({
|
||||
selectedAccountAddresses,
|
||||
addressLastConnectedMap = {},
|
||||
accounts,
|
||||
selectAccounts,
|
||||
selectNewAccountViaModal,
|
||||
cancelPermissionsRequest,
|
||||
permissionsRequestId,
|
||||
targetSubjectMetadata,
|
||||
nativeCurrency,
|
||||
}) => {
|
||||
const [selectedAccounts, setSelectedAccounts] = useState(
|
||||
selectedAccountAddresses,
|
||||
);
|
||||
const t = useI18nContext();
|
||||
|
||||
const handleAccountClick = (address) => {
|
||||
const newSelectedAccounts = new Set(selectedAccounts);
|
||||
if (newSelectedAccounts.has(address)) {
|
||||
newSelectedAccounts.delete(address);
|
||||
} else {
|
||||
newSelectedAccounts.add(address);
|
||||
}
|
||||
setSelectedAccounts(newSelectedAccounts);
|
||||
};
|
||||
|
||||
const selectAll = () => {
|
||||
const newSelectedAccounts = new Set(
|
||||
accounts.map((account) => account.address),
|
||||
);
|
||||
setSelectedAccounts(newSelectedAccounts);
|
||||
};
|
||||
|
||||
const deselectAll = () => {
|
||||
setSelectedAccounts(new Set());
|
||||
};
|
||||
|
||||
const allAreSelected = () => {
|
||||
return accounts.length === selectedAccounts.size;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="permissions-connect-choose-account">
|
||||
<PermissionsConnectHeader
|
||||
iconUrl={targetSubjectMetadata?.iconUrl}
|
||||
iconName={targetSubjectMetadata?.name}
|
||||
headerTitle={t('connectWithMetaMask')}
|
||||
headerText={
|
||||
accounts.length > 0
|
||||
? t('selectAccounts')
|
||||
: t('connectAccountOrCreate')
|
||||
}
|
||||
siteOrigin={targetSubjectMetadata?.origin}
|
||||
/>
|
||||
<AccountList
|
||||
accounts={accounts}
|
||||
selectNewAccountViaModal={selectNewAccountViaModal}
|
||||
addressLastConnectedMap={addressLastConnectedMap}
|
||||
nativeCurrency={nativeCurrency}
|
||||
selectedAccounts={selectedAccounts}
|
||||
allAreSelected={allAreSelected}
|
||||
deselectAll={deselectAll}
|
||||
selectAll={selectAll}
|
||||
handleAccountClick={handleAccountClick}
|
||||
/>
|
||||
<div className="permissions-connect-choose-account__footer-container">
|
||||
<PermissionsConnectFooter />
|
||||
<div className="permissions-connect-choose-account__bottom-buttons">
|
||||
<Button
|
||||
onClick={() => cancelPermissionsRequest(permissionsRequestId)}
|
||||
type="secondary"
|
||||
>
|
||||
{t('cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => selectAccounts(selectedAccounts)}
|
||||
type="primary"
|
||||
disabled={selectedAccounts.size === 0}
|
||||
>
|
||||
{t('next')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
ChooseAccount.propTypes = {
|
||||
/**
|
||||
* Array of user account objects
|
||||
*/
|
||||
accounts: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
address: PropTypes.string,
|
||||
addressLabel: PropTypes.string,
|
||||
lastConnectedDate: PropTypes.string,
|
||||
balance: PropTypes.string,
|
||||
}),
|
||||
).isRequired,
|
||||
/**
|
||||
* Function to select an account
|
||||
*/
|
||||
selectAccounts: PropTypes.func.isRequired,
|
||||
/**
|
||||
* Function to select a new account via modal
|
||||
*/
|
||||
selectNewAccountViaModal: PropTypes.func.isRequired,
|
||||
/**
|
||||
* Native currency of current chain
|
||||
*/
|
||||
nativeCurrency: PropTypes.string.isRequired,
|
||||
/**
|
||||
* A map of the last connected addresses
|
||||
*/
|
||||
addressLastConnectedMap: PropTypes.object,
|
||||
/**
|
||||
* Function to cancel permission request
|
||||
*/
|
||||
cancelPermissionsRequest: PropTypes.func.isRequired,
|
||||
/**
|
||||
* Permission request Id
|
||||
*/
|
||||
permissionsRequestId: PropTypes.string.isRequired,
|
||||
/**
|
||||
* Currently selected account addresses
|
||||
*/
|
||||
selectedAccountAddresses: PropTypes.object.isRequired,
|
||||
/**
|
||||
* Domain data used to display site-origin pill
|
||||
*/
|
||||
targetSubjectMetadata: PropTypes.shape({
|
||||
extensionId: PropTypes.string,
|
||||
iconUrl: PropTypes.string,
|
||||
name: PropTypes.string.isRequired,
|
||||
origin: PropTypes.string.isRequired,
|
||||
subjectType: PropTypes.string,
|
||||
}),
|
||||
};
|
||||
|
||||
export default ChooseAccount;
|
@ -1 +1 @@
|
||||
export { default } from './choose-account.component';
|
||||
export { default } from './choose-account';
|
||||
|
@ -7,143 +7,14 @@
|
||||
margin-right: auto;
|
||||
height: 100%;
|
||||
|
||||
.fa-info-circle {
|
||||
color: $Grey-200;
|
||||
cursor: pointer;
|
||||
margin-left: 8px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.fa-info-circle:hover {
|
||||
color: $Grey-300;
|
||||
}
|
||||
|
||||
@media screen and (min-width: $break-large) {
|
||||
width: 426px;
|
||||
}
|
||||
|
||||
|
||||
&__title {
|
||||
@include H4;
|
||||
}
|
||||
|
||||
&__text,
|
||||
&__text-blue,
|
||||
&__text-grey {
|
||||
@include H6;
|
||||
}
|
||||
|
||||
&__text-blue {
|
||||
color: $primary-blue;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__text-grey {
|
||||
color: $Grey-500;
|
||||
}
|
||||
|
||||
&__accounts-list {
|
||||
flex: 2 1 0;
|
||||
width: 92%;
|
||||
max-height: max-content;
|
||||
border: 1px solid #d0d5da;
|
||||
box-sizing: border-box;
|
||||
border-radius: 8px;
|
||||
margin-top: 8px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
&__accounts-list-header--one-item,
|
||||
&__accounts-list-header--two-items {
|
||||
display: flex;
|
||||
flex: 0;
|
||||
margin-top: 36px;
|
||||
width: 100%;
|
||||
padding-left: 15px;
|
||||
padding-right: 17px;
|
||||
}
|
||||
|
||||
&__accounts-list-header--one-item {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
&__accounts-list-header--two-items {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&__account-info-wrapper {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
&__list-check-box {
|
||||
margin-right: 16px;
|
||||
|
||||
i {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__header-check-box {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
&__select-all {
|
||||
display: flex;
|
||||
margin-left: 16px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__account {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
border-bottom: 1px solid #d2d8dd;
|
||||
justify-content: space-between;
|
||||
|
||||
&:last-of-type {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $Grey-000;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: 16px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
&__label {
|
||||
@include H6;
|
||||
|
||||
color: $Black-100;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__balance {
|
||||
@include H7;
|
||||
|
||||
color: $Grey-500;
|
||||
}
|
||||
|
||||
&__last-connected {
|
||||
@include H8;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
color: $primary-blue;
|
||||
}
|
||||
}
|
||||
|
||||
&__cancel {
|
||||
color: $Red-400;
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ export default class PermissionConnect extends Component {
|
||||
iconUrl: PropTypes.string,
|
||||
name: PropTypes.string.isRequired,
|
||||
origin: PropTypes.string.isRequired,
|
||||
subjectType: PropTypes.string,
|
||||
}),
|
||||
};
|
||||
|
||||
|
@ -21,6 +21,7 @@ import {
|
||||
CONNECT_ROUTE,
|
||||
CONNECT_CONFIRM_PERMISSIONS_ROUTE,
|
||||
} from '../../helpers/constants/routes';
|
||||
import { SUBJECT_TYPES } from '../../../shared/constants/app';
|
||||
import PermissionApproval from './permissions-connect.component';
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
@ -46,7 +47,7 @@ const mapStateToProps = (state, ownProps) => {
|
||||
let targetSubjectMetadata = null;
|
||||
if (origin) {
|
||||
if (subjectMetadata[origin]) {
|
||||
targetSubjectMetadata = { ...subjectMetadata[origin], origin };
|
||||
targetSubjectMetadata = subjectMetadata[origin];
|
||||
} else {
|
||||
const targetUrl = new URL(origin);
|
||||
targetSubjectMetadata = {
|
||||
@ -54,6 +55,7 @@ const mapStateToProps = (state, ownProps) => {
|
||||
origin,
|
||||
iconUrl: null,
|
||||
extensionId: null,
|
||||
subjectType: SUBJECT_TYPES.UNKNOWN,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user