mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-24 19:10:22 +01:00
b98b0b6d01
The app header would sometimes mistakenly not get rendered while on the Home screen. This could happen when a permission request was made while the UI was open, to either the browser action popup or the fullscreen UI. This was caused by faulty logic in the top-level router component. It would hide the app header if there was a pending permission request, presumably because the author assumed that a redirect to the permission flow would shortly follow. This redirect only happens on mount though, not if the UI was already open when the permission request was submitted. The intent of this logic was to hide a brief flash of the app header prior to rendering the permission flow. This brief flash has now been restored, which is unfortunate, but is better than the missing app header bug. We can revisit a solution to removing this flash in the future, hopefully in a manner that avoids this bug and works for all notification UI cases.
287 lines
7.7 KiB
JavaScript
287 lines
7.7 KiB
JavaScript
import { forOwn } from 'lodash'
|
|
import {
|
|
CAVEAT_NAMES,
|
|
} from '../../../app/scripts/controllers/permissions/enums'
|
|
import { getMetaMaskAccountsOrdered, getOriginOfCurrentTab, getSelectedAddress } from '.'
|
|
|
|
// selectors
|
|
|
|
/**
|
|
* Get the permission domains object.
|
|
*
|
|
* @param {Object} state - The current state.
|
|
* @returns {Object} The permissions domains object.
|
|
*/
|
|
export function getPermissionDomains (state) {
|
|
return state.metamask.domains || {}
|
|
}
|
|
|
|
/**
|
|
* Get the permission domains metadata object.
|
|
*
|
|
* @param {Object} state - The current state.
|
|
* @returns {Object} The permission domains metadata object.
|
|
*/
|
|
export function getPermissionDomainsMetadata (state) {
|
|
return state.metamask.domainMetadata || {}
|
|
}
|
|
|
|
/**
|
|
* Selects the permitted accounts from the eth_accounts permission given state
|
|
* and an origin.
|
|
*
|
|
* @param {Object} state - The current state.
|
|
* @param {string} origin - The origin/domain to get the permitted accounts for.
|
|
* @returns {Array<string>} An empty array or an array of accounts.
|
|
*/
|
|
export function getPermittedAccounts (state, origin) {
|
|
return getAccountsFromPermission(
|
|
getAccountsPermissionFromDomain(
|
|
domainSelector(state, origin),
|
|
),
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Selects the permitted accounts from the eth_accounts permission for the
|
|
* origin of the current tab.
|
|
*
|
|
* @param {Object} state - The current state.
|
|
* @returns {Array<string>} An empty array or an array of accounts.
|
|
*/
|
|
export function getPermittedAccountsForCurrentTab (state) {
|
|
return getPermittedAccounts(
|
|
state,
|
|
getOriginOfCurrentTab(state),
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Returns a map of permitted accounts by origin for all origins.
|
|
*
|
|
* @param {Object} state - The current state.
|
|
* @returns {Object} Permitted accounts by origin.
|
|
*/
|
|
export function getPermittedAccountsByOrigin (state) {
|
|
const domains = getPermissionDomains(state)
|
|
return Object.keys(domains).reduce((acc, domainKey) => {
|
|
const accounts = getAccountsFromPermission(
|
|
getAccountsPermissionFromDomain(domains[domainKey]),
|
|
)
|
|
if (accounts.length > 0) {
|
|
acc[domainKey] = accounts
|
|
}
|
|
return acc
|
|
}, {})
|
|
}
|
|
|
|
/**
|
|
* Returns an array of connected domain objects, with the following properties:
|
|
* - extensionId
|
|
* - key (i.e. origin)
|
|
* - name
|
|
* - icon
|
|
*
|
|
* @param {Object} state - The current state.
|
|
* @returns {Array<Object>} An array of connected domain objects.
|
|
*/
|
|
export function getConnectedDomainsForSelectedAddress (state) {
|
|
const {
|
|
selectedAddress,
|
|
} = state.metamask
|
|
const domains = getPermissionDomains(state)
|
|
const domainMetadata = getPermissionDomainsMetadata(state)
|
|
|
|
const connectedDomains = []
|
|
|
|
forOwn(domains, (domainValue, domainKey) => {
|
|
const exposedAccounts = getAccountsFromDomain(domainValue)
|
|
if (!exposedAccounts.includes(selectedAddress)) {
|
|
return
|
|
}
|
|
|
|
const {
|
|
extensionId,
|
|
name,
|
|
icon,
|
|
host,
|
|
} = domainMetadata[domainKey] || {}
|
|
|
|
connectedDomains.push({
|
|
extensionId,
|
|
origin: domainKey,
|
|
name,
|
|
icon,
|
|
host,
|
|
})
|
|
})
|
|
|
|
return connectedDomains
|
|
}
|
|
|
|
/**
|
|
* Returns an object mapping addresses to objects mapping origins to connected
|
|
* domain info. Domain info objects have the following properties:
|
|
* - icon
|
|
* - name
|
|
*
|
|
* @param {Object} state - The current state.
|
|
* @returns {Object} A mapping of addresses to a mapping of origins to
|
|
* connected domain info.
|
|
*/
|
|
export function getAddressConnectedDomainMap (state) {
|
|
const domainMetadata = getPermissionDomainsMetadata(state)
|
|
const accountsMap = getPermittedAccountsByOrigin(state)
|
|
const addressConnectedIconMap = {}
|
|
|
|
Object.keys(accountsMap).forEach((domainKey) => {
|
|
|
|
const { icon, name } = domainMetadata[domainKey] || {}
|
|
|
|
accountsMap[domainKey].forEach((address) => {
|
|
|
|
const nameToRender = name || domainKey
|
|
|
|
addressConnectedIconMap[address] = addressConnectedIconMap[address]
|
|
? { ...addressConnectedIconMap[address], [domainKey]: { icon, name: nameToRender } }
|
|
: { [domainKey]: { icon, name: nameToRender } }
|
|
})
|
|
})
|
|
|
|
return addressConnectedIconMap
|
|
}
|
|
|
|
// selector helpers
|
|
|
|
function getAccountsFromDomain (domain) {
|
|
return getAccountsFromPermission(
|
|
getAccountsPermissionFromDomain(domain),
|
|
)
|
|
}
|
|
|
|
function getAccountsPermissionFromDomain (domain = {}) {
|
|
return (
|
|
Array.isArray(domain.permissions)
|
|
? domain.permissions.find(
|
|
(perm) => perm.parentCapability === 'eth_accounts',
|
|
)
|
|
: {}
|
|
)
|
|
}
|
|
|
|
function getAccountsFromPermission (accountsPermission) {
|
|
const accountsCaveat = getAccountsCaveatFromPermission(accountsPermission)
|
|
return (
|
|
accountsCaveat && Array.isArray(accountsCaveat.value)
|
|
? accountsCaveat.value
|
|
: []
|
|
)
|
|
}
|
|
|
|
function getAccountsCaveatFromPermission (accountsPermission = {}) {
|
|
return (
|
|
Array.isArray(accountsPermission.caveats) &&
|
|
accountsPermission.caveats.find(
|
|
(c) => c.name === CAVEAT_NAMES.exposedAccounts,
|
|
)
|
|
)
|
|
}
|
|
|
|
function domainSelector (state, origin) {
|
|
return origin && state.metamask.domains?.[origin]
|
|
}
|
|
|
|
export function getAccountToConnectToActiveTab (state) {
|
|
const selectedAddress = getSelectedAddress(state)
|
|
const connectedAccounts = getPermittedAccountsForCurrentTab(state)
|
|
|
|
const { metamask: { identities } } = state
|
|
const numberOfAccounts = Object.keys(identities).length
|
|
|
|
if (connectedAccounts.length && connectedAccounts.length !== numberOfAccounts) {
|
|
if (connectedAccounts.findIndex((address) => address === selectedAddress) === -1) {
|
|
return identities[selectedAddress]
|
|
}
|
|
}
|
|
|
|
return undefined
|
|
}
|
|
|
|
export function getOrderedConnectedAccountsForActiveTab (state) {
|
|
const { activeTab, metamask: { permissionsHistory } } = state
|
|
|
|
const permissionsHistoryByAccount = permissionsHistory[activeTab.origin]?.['eth_accounts']?.accounts
|
|
const orderedAccounts = getMetaMaskAccountsOrdered(state)
|
|
const connectedAccounts = getPermittedAccountsForCurrentTab(state)
|
|
|
|
return orderedAccounts
|
|
.filter((account) => connectedAccounts.includes(account.address))
|
|
.map((account) => ({
|
|
...account,
|
|
lastActive: permissionsHistoryByAccount?.[account.address],
|
|
}))
|
|
.sort(({ lastSelected: lastSelectedA }, { lastSelected: lastSelectedB }) => {
|
|
if (lastSelectedA === lastSelectedB) {
|
|
return 0
|
|
} else if (lastSelectedA === undefined) {
|
|
return 1
|
|
} else if (lastSelectedB === undefined) {
|
|
return -1
|
|
}
|
|
|
|
return lastSelectedB - lastSelectedA
|
|
})
|
|
}
|
|
|
|
export function getPermissionsForActiveTab (state) {
|
|
const { activeTab, metamask } = state
|
|
const {
|
|
domains = {},
|
|
} = metamask
|
|
|
|
return domains[activeTab.origin]?.permissions?.map(({ parentCapability }) => {
|
|
return {
|
|
key: parentCapability,
|
|
}
|
|
})
|
|
}
|
|
|
|
export function getLastConnectedInfo (state) {
|
|
const { permissionsHistory = {} } = state.metamask
|
|
return Object.keys(permissionsHistory).reduce((acc, origin) => {
|
|
const ethAccountsHistory = JSON.parse(JSON.stringify(permissionsHistory[origin].eth_accounts))
|
|
return {
|
|
...acc,
|
|
[origin]: ethAccountsHistory,
|
|
}
|
|
}, {})
|
|
}
|
|
|
|
export function getPermissionsMetadataHostCounts (state) {
|
|
const metadata = getPermissionDomainsMetadata(state)
|
|
return Object.values(metadata).reduce((counts, { host }) => {
|
|
if (host) {
|
|
if (counts[host]) {
|
|
counts[host] += 1
|
|
} else {
|
|
counts[host] = 1
|
|
}
|
|
}
|
|
return counts
|
|
}, {})
|
|
}
|
|
|
|
export function getPermissionsRequests (state) {
|
|
return state.metamask.permissionsRequests || []
|
|
}
|
|
|
|
export function getPermissionsRequestCount (state) {
|
|
const permissionsRequests = getPermissionsRequests(state)
|
|
return permissionsRequests.length
|
|
}
|
|
|
|
export function getFirstPermissionRequest (state) {
|
|
const requests = getPermissionsRequests(state)
|
|
return requests && requests[0] ? requests[0] : null
|
|
}
|