mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 09:57:02 +01:00
[FLASK] Add update snap UI (#15143)
* added snap-update folder * addded update route, snap update component, updated permissions connect components * added actions and selectors * updated permissions selectors and updated permissions connect container to have update snap logic * updated translations, added selector, updated request object * updated translations, added update snap permission list component * more fixes * added CSS, redid some HTML * lint fixes * Add missing grantPermissions action * updated button padding * fixes * removed prop type * fix Update & Install wrapping * made changes for forthcoming snap controller PR * removed ununsed imports * updated css * re-added padding rule and removed unused translation messages * addressed comments * add subtext for new permissions * lint fix * removed unused translations * some more changes * fix e2e tests * lint fix * added in delay for e2e tests * Revert "added in delay for e2e tests" This reverts commit 095962a2c0c9de0b0b343d3134bb0787044dd8ce. * fixed routing logic Co-authored-by: Frederik Bolding <frederik.bolding@gmail.com> Co-authored-by: Guillaume Roux <guillaumeroux123@gmail.com>
This commit is contained in:
parent
97b10f96e0
commit
a7179a6b88
6
app/_locales/de/messages.json
generated
6
app/_locales/de/messages.json
generated
@ -285,9 +285,6 @@
|
||||
"approvedAsset": {
|
||||
"message": "Genehmigtes Asset"
|
||||
},
|
||||
"areYouDeveloper": {
|
||||
"message": "Sind Sie ein Entwickler?"
|
||||
},
|
||||
"areYouSure": {
|
||||
"message": "Sind Sie sicher?"
|
||||
},
|
||||
@ -2360,9 +2357,6 @@
|
||||
"message": "Öffnen Sie MetaMask im Vollbildmodus, um Ihren Ledger über WebHID zu verbinden.",
|
||||
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
|
||||
},
|
||||
"openSourceCode": {
|
||||
"message": "Prüfen Sie den Quellcode"
|
||||
},
|
||||
"optional": {
|
||||
"message": "Optional"
|
||||
},
|
||||
|
6
app/_locales/el/messages.json
generated
6
app/_locales/el/messages.json
generated
@ -293,9 +293,6 @@
|
||||
"approvedAsset": {
|
||||
"message": "Εγκεκριμένο περιουσιακό στοιχείο"
|
||||
},
|
||||
"areYouDeveloper": {
|
||||
"message": "Είστε προγραμματιστής;"
|
||||
},
|
||||
"areYouSure": {
|
||||
"message": "Είστε βέβαιος/η;"
|
||||
},
|
||||
@ -2397,9 +2394,6 @@
|
||||
"message": "Ανοίξτε το MetaMask σε πλήρη οθόνη για να συνδέσετε το ledger σας μέσω WebHID.",
|
||||
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
|
||||
},
|
||||
"openSourceCode": {
|
||||
"message": "Ελέγξτε τον πηγαίο κώδικα"
|
||||
},
|
||||
"optional": {
|
||||
"message": "Προαιρετικό"
|
||||
},
|
||||
|
26
app/_locales/en/messages.json
generated
26
app/_locales/en/messages.json
generated
@ -332,7 +332,10 @@
|
||||
"description": "$1 is the symbol of the token for which the user is granting approval"
|
||||
},
|
||||
"approveAndInstall": {
|
||||
"message": "Approve & Install"
|
||||
"message": "Approve & install"
|
||||
},
|
||||
"approveAndUpdate": {
|
||||
"message": "Approve & update"
|
||||
},
|
||||
"approveButtonText": {
|
||||
"message": "Approve"
|
||||
@ -350,8 +353,9 @@
|
||||
"approvedAsset": {
|
||||
"message": "Approved asset"
|
||||
},
|
||||
"areYouDeveloper": {
|
||||
"message": "Are you a developer?"
|
||||
"approvedOn": {
|
||||
"message": "Approved on $1",
|
||||
"description": "$1 is the approval date for a permission"
|
||||
},
|
||||
"areYouSure": {
|
||||
"message": "Are you sure?"
|
||||
@ -2454,9 +2458,6 @@
|
||||
"message": "Open MetaMask in full screen to connect your ledger via WebHID.",
|
||||
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
|
||||
},
|
||||
"openSourceCode": {
|
||||
"message": "Check the source code"
|
||||
},
|
||||
"optional": {
|
||||
"message": "Optional"
|
||||
},
|
||||
@ -2529,6 +2530,12 @@
|
||||
"permissionRequestCapitalized": {
|
||||
"message": "Permission Request"
|
||||
},
|
||||
"permissionRequested": {
|
||||
"message": "Requested now"
|
||||
},
|
||||
"permissionRevoked": {
|
||||
"message": "Revoked in this update"
|
||||
},
|
||||
"permission_accessNetwork": {
|
||||
"message": "Access the Internet.",
|
||||
"description": "The description of the `endowment:network-access` permission."
|
||||
@ -3115,6 +3122,13 @@
|
||||
"snapRequestsPermission": {
|
||||
"message": "This snap is requesting the following permissions:"
|
||||
},
|
||||
"snapUpdate": {
|
||||
"message": "Update Snap"
|
||||
},
|
||||
"snapUpdateExplanation": {
|
||||
"message": "$1 needs a newer version of your snap.",
|
||||
"description": "$1 is the dapp that is requesting an update to the snap."
|
||||
},
|
||||
"snaps": {
|
||||
"message": "Snaps"
|
||||
},
|
||||
|
6
app/_locales/es/messages.json
generated
6
app/_locales/es/messages.json
generated
@ -293,9 +293,6 @@
|
||||
"approvedAsset": {
|
||||
"message": "Activo aprobado"
|
||||
},
|
||||
"areYouDeveloper": {
|
||||
"message": "¿Usted es desarrollador?"
|
||||
},
|
||||
"areYouSure": {
|
||||
"message": "¿Está seguro?"
|
||||
},
|
||||
@ -2397,9 +2394,6 @@
|
||||
"message": "Abra MetaMask en pantalla completa para conectar su Ledger a través de WebHID.",
|
||||
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
|
||||
},
|
||||
"openSourceCode": {
|
||||
"message": "Compruebe el código fuente"
|
||||
},
|
||||
"optional": {
|
||||
"message": "Opcional"
|
||||
},
|
||||
|
6
app/_locales/fr/messages.json
generated
6
app/_locales/fr/messages.json
generated
@ -293,9 +293,6 @@
|
||||
"approvedAsset": {
|
||||
"message": "Actif approuvé"
|
||||
},
|
||||
"areYouDeveloper": {
|
||||
"message": "Êtes-vous un(e) développeur(-se) ?"
|
||||
},
|
||||
"areYouSure": {
|
||||
"message": "En êtes-vous sûr(e) ?"
|
||||
},
|
||||
@ -2397,9 +2394,6 @@
|
||||
"message": "Ouvrez MetaMask en mode plein écran pour connecter votre Ledger via WebHID.",
|
||||
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
|
||||
},
|
||||
"openSourceCode": {
|
||||
"message": "Vérifier le code source"
|
||||
},
|
||||
"optional": {
|
||||
"message": "Facultatif"
|
||||
},
|
||||
|
6
app/_locales/hi/messages.json
generated
6
app/_locales/hi/messages.json
generated
@ -293,9 +293,6 @@
|
||||
"approvedAsset": {
|
||||
"message": "स्वीकृत एसेट"
|
||||
},
|
||||
"areYouDeveloper": {
|
||||
"message": "क्या आप एक डेवलपर हैं?"
|
||||
},
|
||||
"areYouSure": {
|
||||
"message": "क्या आप सुनिश्चित हैं?"
|
||||
},
|
||||
@ -2397,9 +2394,6 @@
|
||||
"message": "अपने लेजर को WebHID के माध्यम से कनेक्ट करने के लिए MetaMask को पूर्ण स्क्रीन में खोलें।",
|
||||
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
|
||||
},
|
||||
"openSourceCode": {
|
||||
"message": "सोर्स कोड जांचें"
|
||||
},
|
||||
"optional": {
|
||||
"message": "वैकल्पिक"
|
||||
},
|
||||
|
6
app/_locales/id/messages.json
generated
6
app/_locales/id/messages.json
generated
@ -293,9 +293,6 @@
|
||||
"approvedAsset": {
|
||||
"message": "Aset yang disetujui"
|
||||
},
|
||||
"areYouDeveloper": {
|
||||
"message": "Anda seorang pengembang?"
|
||||
},
|
||||
"areYouSure": {
|
||||
"message": "Anda yakin?"
|
||||
},
|
||||
@ -2397,9 +2394,6 @@
|
||||
"message": "Buka MetaMask dalam layar penuh untuk menghubungkan ledger Anda melalui WebHID.",
|
||||
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
|
||||
},
|
||||
"openSourceCode": {
|
||||
"message": "Periksa kode sumbernya"
|
||||
},
|
||||
"optional": {
|
||||
"message": "Opsional"
|
||||
},
|
||||
|
6
app/_locales/ja/messages.json
generated
6
app/_locales/ja/messages.json
generated
@ -293,9 +293,6 @@
|
||||
"approvedAsset": {
|
||||
"message": "承認済みのアセット"
|
||||
},
|
||||
"areYouDeveloper": {
|
||||
"message": "開発者の方ですか?"
|
||||
},
|
||||
"areYouSure": {
|
||||
"message": "よろしいですか?"
|
||||
},
|
||||
@ -2397,9 +2394,6 @@
|
||||
"message": "WebHIDでLedgerを接続するには、MetaMaskを全画面モードで開いてください。",
|
||||
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
|
||||
},
|
||||
"openSourceCode": {
|
||||
"message": "ソースコードを確認"
|
||||
},
|
||||
"optional": {
|
||||
"message": "任意"
|
||||
},
|
||||
|
6
app/_locales/ko/messages.json
generated
6
app/_locales/ko/messages.json
generated
@ -293,9 +293,6 @@
|
||||
"approvedAsset": {
|
||||
"message": "승인된 자산"
|
||||
},
|
||||
"areYouDeveloper": {
|
||||
"message": "개발자이신가요?"
|
||||
},
|
||||
"areYouSure": {
|
||||
"message": "확실한가요?"
|
||||
},
|
||||
@ -2397,9 +2394,6 @@
|
||||
"message": "전체 화면에서 MetaMask를 열어 WebHID를 통해 Ledger를 연결합니다.",
|
||||
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
|
||||
},
|
||||
"openSourceCode": {
|
||||
"message": "소스 코드를 확인하세요"
|
||||
},
|
||||
"optional": {
|
||||
"message": "옵션"
|
||||
},
|
||||
|
6
app/_locales/pt/messages.json
generated
6
app/_locales/pt/messages.json
generated
@ -293,9 +293,6 @@
|
||||
"approvedAsset": {
|
||||
"message": "Ativo aprovado"
|
||||
},
|
||||
"areYouDeveloper": {
|
||||
"message": "Você é desenvolvedor?"
|
||||
},
|
||||
"areYouSure": {
|
||||
"message": "Tem certeza?"
|
||||
},
|
||||
@ -2397,9 +2394,6 @@
|
||||
"message": "Abra a MetaMask em tela cheia para conectar sua ledger por meio do WebHID.",
|
||||
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
|
||||
},
|
||||
"openSourceCode": {
|
||||
"message": "Verifique o código-fonte"
|
||||
},
|
||||
"optional": {
|
||||
"message": "Opcional"
|
||||
},
|
||||
|
6
app/_locales/ru/messages.json
generated
6
app/_locales/ru/messages.json
generated
@ -293,9 +293,6 @@
|
||||
"approvedAsset": {
|
||||
"message": "Одобренный актив"
|
||||
},
|
||||
"areYouDeveloper": {
|
||||
"message": "Вы разработчик?"
|
||||
},
|
||||
"areYouSure": {
|
||||
"message": "Вы уверены?"
|
||||
},
|
||||
@ -2397,9 +2394,6 @@
|
||||
"message": "Откройте MetaMask в полноэкранном режиме, чтобы подключить свой леджер через WebHID.",
|
||||
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
|
||||
},
|
||||
"openSourceCode": {
|
||||
"message": "Проверьте исходный код"
|
||||
},
|
||||
"optional": {
|
||||
"message": "Необязательно"
|
||||
},
|
||||
|
6
app/_locales/tl/messages.json
generated
6
app/_locales/tl/messages.json
generated
@ -293,9 +293,6 @@
|
||||
"approvedAsset": {
|
||||
"message": "Aprubadong asset"
|
||||
},
|
||||
"areYouDeveloper": {
|
||||
"message": "Isa ka bang developer?"
|
||||
},
|
||||
"areYouSure": {
|
||||
"message": "Sigurado ka ba?"
|
||||
},
|
||||
@ -2397,9 +2394,6 @@
|
||||
"message": "Buksan ang MetaMask sa buong screen para ikonekta ang ledger mo sa pamamagitan ng WebHID.",
|
||||
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
|
||||
},
|
||||
"openSourceCode": {
|
||||
"message": "Suriin ang code ng pinagmulan"
|
||||
},
|
||||
"optional": {
|
||||
"message": "Opsyonal"
|
||||
},
|
||||
|
6
app/_locales/tr/messages.json
generated
6
app/_locales/tr/messages.json
generated
@ -293,9 +293,6 @@
|
||||
"approvedAsset": {
|
||||
"message": "Onaylanan varlık"
|
||||
},
|
||||
"areYouDeveloper": {
|
||||
"message": "Geliştirici misin?"
|
||||
},
|
||||
"areYouSure": {
|
||||
"message": "Emin misin?"
|
||||
},
|
||||
@ -2397,9 +2394,6 @@
|
||||
"message": "Kayıt defterinizi WebHID üzerinden bağlamak için MetaMask'i tam ekran açın.",
|
||||
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
|
||||
},
|
||||
"openSourceCode": {
|
||||
"message": "Kaynak kodunu kontrol et"
|
||||
},
|
||||
"optional": {
|
||||
"message": "İsteğe bağlı"
|
||||
},
|
||||
|
6
app/_locales/vi/messages.json
generated
6
app/_locales/vi/messages.json
generated
@ -293,9 +293,6 @@
|
||||
"approvedAsset": {
|
||||
"message": "Tài sản được chấp nhận"
|
||||
},
|
||||
"areYouDeveloper": {
|
||||
"message": "Bạn có phải là lập trình viên không?"
|
||||
},
|
||||
"areYouSure": {
|
||||
"message": "Bạn có chắc chắn không?"
|
||||
},
|
||||
@ -2397,9 +2394,6 @@
|
||||
"message": "Mở MetaMask ở chế độ toàn màn hình để kết nối thiết bị Ledger của bạn qua WebHID.",
|
||||
"description": "Shown to the user on the confirm screen when they are viewing MetaMask in a popup window but need to connect their ledger via webhid."
|
||||
},
|
||||
"openSourceCode": {
|
||||
"message": "Kiểm tra mã nguồn"
|
||||
},
|
||||
"optional": {
|
||||
"message": "Không bắt buộc"
|
||||
},
|
||||
|
@ -661,7 +661,10 @@ export default class MetamaskController extends EventEmitter {
|
||||
`${this.permissionController.name}:hasPermissions`,
|
||||
`${this.permissionController.name}:requestPermissions`,
|
||||
`${this.permissionController.name}:revokeAllPermissions`,
|
||||
`${this.permissionController.name}:revokePermissions`,
|
||||
`${this.permissionController.name}:revokePermissionForAllSubjects`,
|
||||
`${this.approvalController.name}:addRequest`,
|
||||
`${this.permissionController.name}:grantPermissions`,
|
||||
'ExecutionService:executeSnap',
|
||||
'ExecutionService:getRpcRequestHandler',
|
||||
'ExecutionService:terminateSnap',
|
||||
@ -709,6 +712,7 @@ export default class MetamaskController extends EventEmitter {
|
||||
},
|
||||
state: initState.SnapController,
|
||||
messenger: snapControllerMessenger,
|
||||
featureFlags: { dappsCanUpdateSnaps: true },
|
||||
});
|
||||
|
||||
this.notificationController = new NotificationController({
|
||||
|
@ -64,7 +64,7 @@ describe('Test Snap bip-44', function () {
|
||||
windowHandles,
|
||||
);
|
||||
await driver.clickElement({
|
||||
text: 'Approve & Install',
|
||||
text: 'Approve & install',
|
||||
tag: 'button',
|
||||
});
|
||||
// deal with permissions popover
|
||||
|
@ -56,7 +56,7 @@ describe('Test Snap Confirm', function () {
|
||||
windowHandles,
|
||||
);
|
||||
await driver.clickElement({
|
||||
text: 'Approve & Install',
|
||||
text: 'Approve & install',
|
||||
tag: 'button',
|
||||
});
|
||||
|
||||
|
@ -56,7 +56,7 @@ describe('Test Snap Error', function () {
|
||||
windowHandles,
|
||||
);
|
||||
await driver.clickElement({
|
||||
text: 'Approve & Install',
|
||||
text: 'Approve & install',
|
||||
tag: 'button',
|
||||
});
|
||||
|
||||
|
@ -64,7 +64,7 @@ describe('Test Snap manageState', function () {
|
||||
windowHandles,
|
||||
);
|
||||
await driver.clickElement({
|
||||
text: 'Approve & Install',
|
||||
text: 'Approve & install',
|
||||
tag: 'button',
|
||||
});
|
||||
|
||||
|
@ -64,7 +64,7 @@ describe('Test Snap Notification', function () {
|
||||
windowHandles,
|
||||
);
|
||||
await driver.clickElement({
|
||||
text: 'Approve & Install',
|
||||
text: 'Approve & install',
|
||||
tag: 'button',
|
||||
});
|
||||
|
||||
|
@ -31,10 +31,11 @@
|
||||
@import 'edit-gas-fee-popover/network-statistics/status-slider/index';
|
||||
@import 'edit-gas-fee-popover/edit-gas-tooltip/index';
|
||||
@import 'flask/experimental-area/index';
|
||||
@import 'flask/snaps-authorship-pill/index';
|
||||
@import 'flask/snap-install-warning/index';
|
||||
@import 'flask/snap-remove-warning/index';
|
||||
@import 'flask/snap-settings-card/index';
|
||||
@import 'flask/snaps-authorship-pill/index';
|
||||
@import 'flask/update-snap-permission-list/index';
|
||||
@import 'gas-customization/gas-modal-page-container/index';
|
||||
@import 'gas-customization/gas-price-button-group/index';
|
||||
@import 'gas-customization/index';
|
||||
|
@ -15,8 +15,14 @@ const snapIdPrefixes = ['npm:', 'local:'];
|
||||
|
||||
const SnapsAuthorshipPill = ({ snapId, version, className }) => {
|
||||
// @todo Use getSnapPrefix from snaps-skunkworks when possible
|
||||
const snapPrefix = snapIdPrefixes.find((prefix) => snapId.startsWith(prefix));
|
||||
const packageName = snapId.replace(snapPrefix, '');
|
||||
// We're using optional chaining with snapId, because with the current implementation
|
||||
// of snap update in the snap controller, we do not have reference to snapId when an
|
||||
// update request is rejected because the reference comes from the request itself and not subject metadata
|
||||
// like it is done with snap install
|
||||
const snapPrefix = snapIdPrefixes.find((prefix) =>
|
||||
snapId?.startsWith(prefix),
|
||||
);
|
||||
const packageName = snapId?.replace(snapPrefix, '');
|
||||
const isNPM = snapPrefix === 'npm:';
|
||||
const url = isNPM
|
||||
? `https://www.npmjs.com/package/${packageName}`
|
||||
|
@ -0,0 +1 @@
|
||||
export { default } from './update-snap-permission-list';
|
@ -0,0 +1,47 @@
|
||||
.update-snap-permission-list {
|
||||
width: 100%;
|
||||
|
||||
.approved-permission,
|
||||
.new-permission,
|
||||
.revoked-permission {
|
||||
@include H6;
|
||||
|
||||
width: 100%;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid var(--color-border-default);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
color: var(--color-text-default);
|
||||
|
||||
i {
|
||||
display: block;
|
||||
padding: 16px;
|
||||
min-width: 16px;
|
||||
min-height: 16px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.approved-permission {
|
||||
color: var(--color-success-default);
|
||||
}
|
||||
|
||||
.revoked-permission {
|
||||
color: var(--color-error-alternative);
|
||||
}
|
||||
|
||||
.new-permission {
|
||||
color: var(--color-info-default);
|
||||
}
|
||||
|
||||
.permission-description {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.permission-description-subtext {
|
||||
@include H7;
|
||||
}
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { getPermissionDescription } from '../../../../helpers/utils/permission';
|
||||
import { useI18nContext } from '../../../../hooks/useI18nContext';
|
||||
import { formatDate } from '../../../../helpers/utils/util';
|
||||
import Typography from '../../../ui/typography/typography';
|
||||
import { COLORS } from '../../../../helpers/constants/design-system';
|
||||
|
||||
export default function UpdateSnapPermissionList({
|
||||
approvedPermissions,
|
||||
revokedPermissions,
|
||||
newPermissions,
|
||||
}) {
|
||||
const t = useI18nContext();
|
||||
|
||||
const ApprovedPermissions = () => {
|
||||
return Object.keys(approvedPermissions).map((approvedPermission) => {
|
||||
const { label, rightIcon } = getPermissionDescription(
|
||||
t,
|
||||
approvedPermission,
|
||||
);
|
||||
const { date } = approvedPermissions[approvedPermission];
|
||||
const formattedDate = formatDate(date, 'yyyy-MM-dd');
|
||||
return (
|
||||
<div className="approved-permission" key={approvedPermission}>
|
||||
<i className="fas fa-check" />
|
||||
<div className="permission-description">
|
||||
{label}
|
||||
<Typography
|
||||
color={COLORS.TEXT_ALTERNATIVE}
|
||||
className="permission-description-subtext"
|
||||
boxProps={{ paddingTop: 1 }}
|
||||
>
|
||||
{t('approvedOn', [formattedDate])}
|
||||
</Typography>
|
||||
</div>
|
||||
{rightIcon && <i className={rightIcon} />}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const RevokedPermissions = () => {
|
||||
return Object.keys(revokedPermissions).map((revokedPermission) => {
|
||||
const { label, rightIcon } = getPermissionDescription(
|
||||
t,
|
||||
revokedPermission,
|
||||
);
|
||||
return (
|
||||
<div className="revoked-permission" key={revokedPermission}>
|
||||
<i className="fas fa-x" />
|
||||
<div className="permission-description">
|
||||
{label}
|
||||
<Typography
|
||||
color={COLORS.TEXT_ALTERNATIVE}
|
||||
boxProps={{ paddingTop: 1 }}
|
||||
className="permission-description-subtext"
|
||||
>
|
||||
{t('permissionRevoked')}
|
||||
</Typography>
|
||||
</div>
|
||||
{rightIcon && <i className={rightIcon} />}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const NewPermissions = () => {
|
||||
return Object.keys(newPermissions).map((newPermission) => {
|
||||
const { label, rightIcon } = getPermissionDescription(t, newPermission);
|
||||
return (
|
||||
<div className="new-permission" key={newPermission}>
|
||||
<i className="fas fa-arrow-right" />
|
||||
<div className="permission-description">
|
||||
{label}
|
||||
<Typography
|
||||
color={COLORS.TEXT_ALTERNATIVE}
|
||||
boxProps={{ paddingTop: 1 }}
|
||||
className="permission-description-subtext"
|
||||
>
|
||||
{t('permissionRequested')}
|
||||
</Typography>
|
||||
</div>
|
||||
{rightIcon && <i className={rightIcon} />}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="update-snap-permission-list">
|
||||
<NewPermissions />
|
||||
<ApprovedPermissions />
|
||||
<RevokedPermissions />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
UpdateSnapPermissionList.propTypes = {
|
||||
/**
|
||||
* Permissions that have already been approved
|
||||
*/
|
||||
approvedPermissions: PropTypes.object.isRequired,
|
||||
/**
|
||||
* Previously used permissions that are now revoked
|
||||
*/
|
||||
revokedPermissions: PropTypes.object.isRequired,
|
||||
/**
|
||||
* New permissions that are being requested
|
||||
*/
|
||||
newPermissions: PropTypes.object.isRequired,
|
||||
};
|
@ -29,7 +29,7 @@ export default class PermissionsConnectHeader extends Component {
|
||||
rightIcon: PropTypes.node,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
snapVersion: PropTypes.string,
|
||||
isSnapInstall: PropTypes.bool,
|
||||
isSnapInstallOrUpdate: PropTypes.bool,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
};
|
||||
|
||||
@ -47,12 +47,12 @@ export default class PermissionsConnectHeader extends Component {
|
||||
siteOrigin,
|
||||
rightIcon,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
isSnapInstall,
|
||||
isSnapInstallOrUpdate,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} = this.props;
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
if (isSnapInstall) {
|
||||
if (isSnapInstallOrUpdate) {
|
||||
return null;
|
||||
}
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
@ -79,7 +79,7 @@ export default class PermissionsConnectHeader extends Component {
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
siteOrigin,
|
||||
snapVersion,
|
||||
isSnapInstall,
|
||||
isSnapInstallOrUpdate,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} = this.props;
|
||||
return (
|
||||
@ -93,7 +93,7 @@ export default class PermissionsConnectHeader extends Component {
|
||||
<div className="permissions-connect-header__title">{headerTitle}</div>
|
||||
{
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
isSnapInstall && (
|
||||
isSnapInstallOrUpdate && (
|
||||
<SnapsAuthorshipPill snapId={siteOrigin} version={snapVersion} />
|
||||
)
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
@ -35,6 +35,7 @@ const CONNECT_ROUTE = '/connect';
|
||||
const CONNECT_CONFIRM_PERMISSIONS_ROUTE = '/confirm-permissions';
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
const CONNECT_SNAP_INSTALL_ROUTE = '/snap-install';
|
||||
const CONNECT_SNAP_UPDATE_ROUTE = '/snap-update';
|
||||
const NOTIFICATIONS_ROUTE = '/notifications';
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
const CONNECTED_ROUTE = '/connected';
|
||||
@ -250,6 +251,7 @@ export {
|
||||
CONNECT_CONFIRM_PERMISSIONS_ROUTE,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
CONNECT_SNAP_INSTALL_ROUTE,
|
||||
CONNECT_SNAP_UPDATE_ROUTE,
|
||||
NOTIFICATIONS_ROUTE,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
CONNECTED_ROUTE,
|
||||
|
@ -5,6 +5,9 @@ import {
|
||||
activeTabHasPermissions,
|
||||
getCurrentEthBalance,
|
||||
getFirstPermissionRequest,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
getFirstSnapUpdateRequest,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
getIsMainnet,
|
||||
getOriginOfCurrentTab,
|
||||
getTotalUnapprovedCount,
|
||||
@ -86,9 +89,18 @@ const mapStateToProps = (state) => {
|
||||
const isPopup = envType === ENVIRONMENT_TYPE_POPUP;
|
||||
const isNotification = envType === ENVIRONMENT_TYPE_NOTIFICATION;
|
||||
|
||||
const firstPermissionsRequest = getFirstPermissionRequest(state);
|
||||
const firstPermissionsRequestId =
|
||||
firstPermissionsRequest?.metadata.id || null;
|
||||
let firstPermissionsRequest, firstPermissionsRequestId;
|
||||
firstPermissionsRequest = getFirstPermissionRequest(state);
|
||||
firstPermissionsRequestId = firstPermissionsRequest?.metadata.id || null;
|
||||
|
||||
// getFirstPermissionRequest should be updated with snap update logic once we hit main extension release
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
if (!firstPermissionsRequest) {
|
||||
firstPermissionsRequest = getFirstSnapUpdateRequest(state);
|
||||
firstPermissionsRequestId = firstPermissionsRequest?.metadata.id || null;
|
||||
}
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
||||
const originOfCurrentTab = getOriginOfCurrentTab(state);
|
||||
const shouldShowWeb3ShimUsageNotification =
|
||||
|
@ -9,19 +9,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.source-code {
|
||||
@include H7;
|
||||
|
||||
display: flex;
|
||||
color: var(--color-text-alternative);
|
||||
|
||||
.link {
|
||||
color: var(--color-primary-default);
|
||||
margin-inline-start: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.page-container__footer {
|
||||
width: 100%;
|
||||
margin-top: 12px;
|
||||
|
@ -68,7 +68,7 @@ export default function SnapInstall({
|
||||
headerTitle={t('snapInstall')}
|
||||
headerText={null} // TODO(ritave): Add header text when snaps support description
|
||||
siteOrigin={targetSubjectMetadata.origin}
|
||||
isSnapInstall
|
||||
isSnapInstallOrUpdate
|
||||
snapVersion={targetSubjectMetadata.version}
|
||||
boxProps={{ alignItems: ALIGN_ITEMS.CENTER }}
|
||||
/>
|
||||
@ -90,31 +90,9 @@ export default function SnapInstall({
|
||||
alignItems={ALIGN_ITEMS.CENTER}
|
||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||
>
|
||||
{targetSubjectMetadata.sourceCode ? (
|
||||
<>
|
||||
<div className="source-code">
|
||||
<div className="text">{t('areYouDeveloper')}</div>
|
||||
<div
|
||||
className="link"
|
||||
onClick={() =>
|
||||
global.platform.openTab({
|
||||
url: targetSubjectMetadata.sourceCode,
|
||||
})
|
||||
}
|
||||
>
|
||||
{t('openSourceCode')}
|
||||
</div>
|
||||
</div>
|
||||
<Box paddingBottom={4}>
|
||||
<PermissionsConnectFooter />
|
||||
</Box>
|
||||
</>
|
||||
) : (
|
||||
<Box className="snap-install__footer--no-source-code" paddingTop={4}>
|
||||
<PermissionsConnectFooter />
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<PageContainerFooter
|
||||
cancelButtonType="default"
|
||||
onCancel={onCancel}
|
||||
|
1
ui/pages/permissions-connect/flask/snap-update/index.js
Normal file
1
ui/pages/permissions-connect/flask/snap-update/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './snap-update';
|
22
ui/pages/permissions-connect/flask/snap-update/index.scss
Normal file
22
ui/pages/permissions-connect/flask/snap-update/index.scss
Normal file
@ -0,0 +1,22 @@
|
||||
.snap-update {
|
||||
box-shadow: none;
|
||||
|
||||
.update-snap-permission-list {
|
||||
padding: 0 24px;
|
||||
|
||||
.new-permission,
|
||||
.approved-permission,
|
||||
.revoked-permission {
|
||||
padding: 8px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.page-container__footer {
|
||||
width: 100%;
|
||||
margin-top: 12px;
|
||||
|
||||
button {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
}
|
||||
}
|
138
ui/pages/permissions-connect/flask/snap-update/snap-update.js
Normal file
138
ui/pages/permissions-connect/flask/snap-update/snap-update.js
Normal file
@ -0,0 +1,138 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { PageContainerFooter } from '../../../../components/ui/page-container';
|
||||
import PermissionsConnectFooter from '../../../../components/app/permissions-connect-footer';
|
||||
import PermissionConnectHeader from '../../../../components/app/permissions-connect-header';
|
||||
import { useI18nContext } from '../../../../hooks/useI18nContext';
|
||||
import SnapInstallWarning from '../../../../components/app/flask/snap-install-warning';
|
||||
import Box from '../../../../components/ui/box/box';
|
||||
import {
|
||||
ALIGN_ITEMS,
|
||||
BLOCK_SIZES,
|
||||
BORDER_STYLE,
|
||||
FLEX_DIRECTION,
|
||||
JUSTIFY_CONTENT,
|
||||
TYPOGRAPHY,
|
||||
} from '../../../../helpers/constants/design-system';
|
||||
import Typography from '../../../../components/ui/typography';
|
||||
import UpdateSnapPermissionList from '../../../../components/app/flask/update-snap-permission-list';
|
||||
|
||||
export default function SnapUpdate({
|
||||
request,
|
||||
approveSnapUpdate,
|
||||
rejectSnapUpdate,
|
||||
targetSubjectMetadata,
|
||||
}) {
|
||||
const t = useI18nContext();
|
||||
|
||||
const [isShowingWarning, setIsShowingWarning] = useState(false);
|
||||
|
||||
const onCancel = useCallback(
|
||||
() => rejectSnapUpdate(request.metadata.id),
|
||||
[request, rejectSnapUpdate],
|
||||
);
|
||||
|
||||
const onSubmit = useCallback(
|
||||
() => approveSnapUpdate(request),
|
||||
[request, approveSnapUpdate],
|
||||
);
|
||||
|
||||
const shouldShowWarning = useMemo(
|
||||
() =>
|
||||
Boolean(
|
||||
request.permissions &&
|
||||
Object.keys(request.permissions).find((v) =>
|
||||
v.startsWith('snap_getBip44Entropy_'),
|
||||
),
|
||||
),
|
||||
[request.permissions],
|
||||
);
|
||||
|
||||
return (
|
||||
<Box
|
||||
className="page-container snap-update"
|
||||
justifyContent={JUSTIFY_CONTENT.SPACE_BETWEEN}
|
||||
height={BLOCK_SIZES.FULL}
|
||||
borderStyle={BORDER_STYLE.NONE}
|
||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||
>
|
||||
<Box
|
||||
className="headers"
|
||||
alignItems={ALIGN_ITEMS.CENTER}
|
||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||
>
|
||||
<PermissionConnectHeader
|
||||
icon={targetSubjectMetadata.iconUrl}
|
||||
iconName={targetSubjectMetadata.name}
|
||||
headerTitle={t('snapUpdate')}
|
||||
headerText={null} // TODO(ritave): Add header text when snaps support description
|
||||
siteOrigin={request.snapId}
|
||||
isSnapInstallOrUpdate
|
||||
snapVersion={request.newVersion}
|
||||
boxProps={{ alignItems: ALIGN_ITEMS.CENTER }}
|
||||
/>
|
||||
<Typography
|
||||
boxProps={{
|
||||
padding: [4, 4, 0, 4],
|
||||
}}
|
||||
variant={TYPOGRAPHY.H7}
|
||||
tag="span"
|
||||
>
|
||||
{t('snapUpdateExplanation', [`${request.metadata.dappOrigin}`])}
|
||||
</Typography>
|
||||
<Typography
|
||||
boxProps={{
|
||||
padding: [2, 4, 0, 4],
|
||||
}}
|
||||
variant={TYPOGRAPHY.H7}
|
||||
tag="span"
|
||||
>
|
||||
{t('snapRequestsPermission')}
|
||||
</Typography>
|
||||
<UpdateSnapPermissionList
|
||||
approvedPermissions={request.approvedPermissions || {}}
|
||||
revokedPermissions={request.unusedPermissions || {}}
|
||||
newPermissions={request.newPermissions || {}}
|
||||
/>
|
||||
</Box>
|
||||
<Box
|
||||
className="footers"
|
||||
alignItems={ALIGN_ITEMS.CENTER}
|
||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||
>
|
||||
<Box className="snap-update__footer--no-source-code" paddingTop={4}>
|
||||
<PermissionsConnectFooter />
|
||||
</Box>
|
||||
<PageContainerFooter
|
||||
cancelButtonType="default"
|
||||
onCancel={onCancel}
|
||||
cancelText={t('cancel')}
|
||||
onSubmit={
|
||||
shouldShowWarning ? () => setIsShowingWarning(true) : onSubmit
|
||||
}
|
||||
submitText={t('approveAndUpdate')}
|
||||
/>
|
||||
</Box>
|
||||
{isShowingWarning && (
|
||||
<SnapInstallWarning
|
||||
onCancel={() => setIsShowingWarning(false)}
|
||||
onSubmit={onSubmit}
|
||||
snapName={targetSubjectMetadata.name}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
SnapUpdate.propTypes = {
|
||||
request: PropTypes.object.isRequired,
|
||||
approveSnapUpdate: PropTypes.func.isRequired,
|
||||
rejectSnapUpdate: PropTypes.func.isRequired,
|
||||
targetSubjectMetadata: PropTypes.shape({
|
||||
iconUrl: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
origin: PropTypes.string.isRequired,
|
||||
sourceCode: PropTypes.string,
|
||||
version: PropTypes.string,
|
||||
}).isRequired,
|
||||
};
|
@ -1,5 +1,6 @@
|
||||
@import 'choose-account/index';
|
||||
@import 'flask/snap-install/index';
|
||||
@import 'flask/snap-update/index';
|
||||
@import 'redirect/index';
|
||||
|
||||
.permissions-connect {
|
||||
|
@ -10,6 +10,7 @@ import ChooseAccount from './choose-account';
|
||||
import PermissionsRedirect from './redirect';
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
import SnapInstall from './flask/snap-install';
|
||||
import SnapUpdate from './flask/snap-update';
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
||||
const APPROVE_TIMEOUT = MILLISECOND * 1200;
|
||||
@ -35,6 +36,7 @@ export default class PermissionConnect extends Component {
|
||||
confirmPermissionPath: PropTypes.string.isRequired,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
snapInstallPath: PropTypes.string.isRequired,
|
||||
snapUpdatePath: PropTypes.string.isRequired,
|
||||
isSnap: PropTypes.bool.isRequired,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
totalPages: PropTypes.string.isRequired,
|
||||
@ -90,6 +92,7 @@ export default class PermissionConnect extends Component {
|
||||
confirmPermissionPath,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
snapInstallPath,
|
||||
snapUpdatePath,
|
||||
isSnap,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
getCurrentWindowTab,
|
||||
@ -114,7 +117,9 @@ export default class PermissionConnect extends Component {
|
||||
if (history.location.pathname === connectPath && !isRequestingAccounts) {
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
if (isSnap) {
|
||||
history.push(snapInstallPath);
|
||||
history.push(
|
||||
permissionsRequest.newPermissions ? snapUpdatePath : snapInstallPath,
|
||||
);
|
||||
} else {
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
history.push(confirmPermissionPath);
|
||||
@ -229,6 +234,7 @@ export default class PermissionConnect extends Component {
|
||||
confirmPermissionPath,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
snapInstallPath,
|
||||
snapUpdatePath,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} = this.props;
|
||||
const {
|
||||
@ -313,6 +319,29 @@ export default class PermissionConnect extends Component {
|
||||
{
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
}
|
||||
{
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
}
|
||||
<Route
|
||||
path={snapUpdatePath}
|
||||
exact
|
||||
render={() => (
|
||||
<SnapUpdate
|
||||
request={permissionsRequest || {}}
|
||||
approveSnapUpdate={(...args) => {
|
||||
approvePermissionsRequest(...args);
|
||||
this.redirect(true);
|
||||
}}
|
||||
rejectSnapUpdate={(requestId) =>
|
||||
this.cancelPermissionsRequest(requestId)
|
||||
}
|
||||
targetSubjectMetadata={targetSubjectMetadata}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
}
|
||||
</Switch>
|
||||
)}
|
||||
</div>
|
||||
|
@ -5,6 +5,9 @@ import {
|
||||
getLastConnectedInfo,
|
||||
getPermissionsRequests,
|
||||
getSelectedAddress,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
getSnapUpdateRequests,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
getTargetSubjectMetadata,
|
||||
} from '../../selectors';
|
||||
import { getNativeCurrency } from '../../ducks/metamask/metamask';
|
||||
@ -22,6 +25,7 @@ import {
|
||||
CONNECT_CONFIRM_PERMISSIONS_ROUTE,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
CONNECT_SNAP_INSTALL_ROUTE,
|
||||
CONNECT_SNAP_UPDATE_ROUTE,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} from '../../helpers/constants/routes';
|
||||
import { SUBJECT_TYPES } from '../../../shared/constants/app';
|
||||
@ -34,7 +38,13 @@ const mapStateToProps = (state, ownProps) => {
|
||||
},
|
||||
location: { pathname },
|
||||
} = ownProps;
|
||||
const permissionsRequests = getPermissionsRequests(state);
|
||||
let permissionsRequests = getPermissionsRequests(state);
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
permissionsRequests = [
|
||||
...permissionsRequests,
|
||||
...getSnapUpdateRequests(state),
|
||||
];
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
const currentAddress = getSelectedAddress(state);
|
||||
|
||||
const permissionsRequest = permissionsRequests.find(
|
||||
@ -42,7 +52,7 @@ const mapStateToProps = (state, ownProps) => {
|
||||
);
|
||||
|
||||
const isRequestingAccounts = Boolean(
|
||||
permissionsRequest?.permissions.eth_accounts,
|
||||
permissionsRequest?.permissions?.eth_accounts,
|
||||
);
|
||||
|
||||
const { metadata = {} } = permissionsRequest || {};
|
||||
@ -77,6 +87,7 @@ const mapStateToProps = (state, ownProps) => {
|
||||
const confirmPermissionPath = `${CONNECT_ROUTE}/${permissionsRequestId}${CONNECT_CONFIRM_PERMISSIONS_ROUTE}`;
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
const snapInstallPath = `${CONNECT_ROUTE}/${permissionsRequestId}${CONNECT_SNAP_INSTALL_ROUTE}`;
|
||||
const snapUpdatePath = `${CONNECT_ROUTE}/${permissionsRequestId}${CONNECT_SNAP_UPDATE_ROUTE}`;
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
||||
let totalPages = 1 + isRequestingAccounts;
|
||||
@ -91,7 +102,7 @@ const mapStateToProps = (state, ownProps) => {
|
||||
} else if (pathname === confirmPermissionPath) {
|
||||
page = isRequestingAccounts ? '2' : '1';
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
} else if (pathname === snapInstallPath) {
|
||||
} else if (pathname === snapInstallPath || pathname === snapUpdatePath) {
|
||||
page = isRequestingAccounts ? '3' : '2';
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} else {
|
||||
@ -103,6 +114,7 @@ const mapStateToProps = (state, ownProps) => {
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
isSnap,
|
||||
snapInstallPath,
|
||||
snapUpdatePath,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
permissionsRequest,
|
||||
permissionsRequestId,
|
||||
|
@ -280,6 +280,19 @@ export function getLastConnectedInfo(state) {
|
||||
}, {});
|
||||
}
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
export function getSnapUpdateRequests(state) {
|
||||
return Object.values(state.metamask.pendingApprovals)
|
||||
.filter(({ type }) => type === 'wallet_updateSnap')
|
||||
.map(({ requestData }) => requestData);
|
||||
}
|
||||
|
||||
export function getFirstSnapUpdateRequest(state) {
|
||||
const requests = getSnapUpdateRequests(state);
|
||||
return requests && requests[0] ? requests[0] : null;
|
||||
}
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
||||
export function getPermissionsRequests(state) {
|
||||
return Object.values(state.metamask.pendingApprovals)
|
||||
.filter(({ type }) => type === 'wallet_requestPermissions')
|
||||
@ -290,3 +303,7 @@ export function getFirstPermissionRequest(state) {
|
||||
const requests = getPermissionsRequests(state);
|
||||
return requests && requests[0] ? requests[0] : null;
|
||||
}
|
||||
|
||||
export function getPermissions(state, origin) {
|
||||
return getPermissionSubjects(state)[origin].permissions;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user