1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-22 09:57:02 +01:00

[FLASK] Add updated version of the Snaps settings UI (#18438)

* Add updated version of the Snaps list UI

Add more changes to match the designs of snaps list

Add next design iteration for snaps list

Update icons, sizes and pointer behaviour

Add redesign for snap settings page

Refactor and improve designs

Fix unit tests and refactor code

Fix e2e test

Fix lint

Update margin values

Add CSS override for connected sites list and update margins

Update paddings as requested

Fix vertical alignment of links

Fix tooltip position on the enable button

Add usage of getSnapName function for displaying snap names

Fix e2e tests and update date format for snap install date

Improve unit test for snap-settings-card

Change installation info logic

Update mocked state for snap

Add tests for ViewSnap component, refactor and update mocked state

Add check for version info

Change Snaps icon in Settings

Refactor Snaps list to use selector

Add handling in case of missing version history

* Fix icon ref

* Remove console logs

* Remove onClick from selector

* Add code fencing for imports in selectors.js
This commit is contained in:
David Drazic 2023-04-24 12:21:37 +02:00 committed by GitHub
parent 1452a5a8ce
commit ff96836871
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 454 additions and 1107 deletions

View File

@ -695,10 +695,6 @@
"message": "$1 ist mit keiner Site verbunden.",
"description": "$1 is the account name"
},
"connectedSnapSites": {
"message": "$1-Snap ist mit diesen Sites verbunden. Sie haben Zugriff auf die oben aufgeführten Berechtigungen.",
"description": "$1 represents the name of the snap"
},
"connecting": {
"message": "Verbinden..."
},
@ -1355,18 +1351,6 @@
"message": "Dateiimport fehlgeschlagen? Bitte hier klicken!",
"description": "Helps user import their account from a JSON file"
},
"flaskSnapSettingsCardButtonCta": {
"message": "Details ansehen",
"description": "Call to action a user can take to see more information about the snap that is installed"
},
"flaskSnapSettingsCardDateAddedOn": {
"message": "Hinzugefügt am",
"description": "Start of the sentence describing when and where snap was added"
},
"flaskSnapSettingsCardFrom": {
"message": "von",
"description": "Part of the sentence describing when and where snap was added"
},
"flaskWelcomeUninstall": {
"message": "Sie sollten diese Erweiterung deinstallieren",
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
@ -3230,10 +3214,6 @@
"settingsSearchMatchingNotFound": {
"message": "Keine passenden Ergebnisse gefunden."
},
"shorthandVersion": {
"message": "v$1",
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
},
"show": {
"message": "Zeigen"
},
@ -3307,14 +3287,6 @@
"smartTransaction": {
"message": "Intelligente Transaktionen"
},
"snapAccess": {
"message": "$1-Snap hat Zugriff auf:",
"description": "$1 represents the name of the snap"
},
"snapAdded": {
"message": "Am $1 von $2 hinzugefügt",
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
},
"snapContent": {
"message": "Diese Inhalte stammen von $1",
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
@ -3351,9 +3323,6 @@
"snapsSettingsDescription": {
"message": "Verwalten Sie Ihre Snaps"
},
"snapsStatus": {
"message": "Snap-Status ist von der Aktivität abhängig."
},
"snapsToggle": {
"message": "Ein Snap wird nur ausgeführt, wenn er aktiviert ist"
},

View File

@ -695,10 +695,6 @@
"message": "$1 δεν είναι συνδεδεμένο με καμία τοποθεσία.",
"description": "$1 is the account name"
},
"connectedSnapSites": {
"message": "Το snap $1 συνδέεται με αυτούς τους ιστότοπους. Έχουν πρόσβαση στις παραπάνω άδειες.",
"description": "$1 represents the name of the snap"
},
"connecting": {
"message": "Σύνδεση..."
},
@ -1355,18 +1351,6 @@
"message": "Η εισαγωγή αρχείων δεν λειτουργεί; Κάντε κλικ εδώ!",
"description": "Helps user import their account from a JSON file"
},
"flaskSnapSettingsCardButtonCta": {
"message": "Προβολή λεπτομερειών",
"description": "Call to action a user can take to see more information about the snap that is installed"
},
"flaskSnapSettingsCardDateAddedOn": {
"message": "Προστέθηκε στις",
"description": "Start of the sentence describing when and where snap was added"
},
"flaskSnapSettingsCardFrom": {
"message": "από",
"description": "Part of the sentence describing when and where snap was added"
},
"flaskWelcomeUninstall": {
"message": "θα πρέπει να καταργήσετε την εγκατάσταση αυτής της επέκτασης",
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
@ -3230,10 +3214,6 @@
"settingsSearchMatchingNotFound": {
"message": "Δε βρέθηκαν αποτελέσματα που να ταιριάζουν."
},
"shorthandVersion": {
"message": "v$1",
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
},
"show": {
"message": "Εμφάνιση"
},
@ -3307,14 +3287,6 @@
"smartTransaction": {
"message": "Έξυπνη Συναλλαγή"
},
"snapAccess": {
"message": "Το snap $1 έχει πρόσβαση σε:",
"description": "$1 represents the name of the snap"
},
"snapAdded": {
"message": "Προστέθηκε στις $1 από $2",
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
},
"snapContent": {
"message": "Αυτό το περιεχόμενο προέρχεται από το $1",
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
@ -3351,9 +3323,6 @@
"snapsSettingsDescription": {
"message": "Διαχειριστείτε τα Snaps σας"
},
"snapsStatus": {
"message": "Η κατάσταση του Snap εξαρτάται από τη δραστηριότητα."
},
"snapsToggle": {
"message": "Ένα snap θα εκτελεστεί μόνο εάν είναι ενεργοποιημένο"
},

View File

@ -771,10 +771,6 @@
"message": "$1 is not connected to any sites.",
"description": "$1 is the account name"
},
"connectedSnapSites": {
"message": "$1 snap is connected to these sites. They have access to the permissions listed above.",
"description": "$1 represents the name of the snap"
},
"connecting": {
"message": "Connecting..."
},
@ -1328,6 +1324,12 @@
"enableSmartTransactions": {
"message": "Enable smart transactions"
},
"enableSnap": {
"message": "Enable snap"
},
"enableSnapDescription": {
"message": "Your installed snap will only have access to its permissions and run if its enabled."
},
"enableToken": {
"message": "enable $1",
"description": "$1 is a token symbol, e.g. ETH"
@ -1478,18 +1480,6 @@
"fileTooBig": {
"message": "The dropped file is too big."
},
"flaskSnapSettingsCardButtonCta": {
"message": "See details",
"description": "Call to action a user can take to see more information about the snap that is installed"
},
"flaskSnapSettingsCardDateAddedOn": {
"message": "Added on",
"description": "Start of the sentence describing when and where snap was added"
},
"flaskSnapSettingsCardFrom": {
"message": "from",
"description": "Part of the sentence describing when and where snap was added"
},
"flaskWelcomeUninstall": {
"message": "you should uninstall this extension",
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
@ -3574,10 +3564,6 @@
"settingsSearchMatchingNotFound": {
"message": "No matching results found."
},
"shorthandVersion": {
"message": "v$1",
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
},
"show": {
"message": "Show"
},
@ -3654,14 +3640,6 @@
"smartTransaction": {
"message": "Smart transaction"
},
"snapAccess": {
"message": "$1 snap has access to:",
"description": "$1 represents the name of the snap"
},
"snapAdded": {
"message": "Added on $1 from $2",
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
},
"snapContent": {
"message": "This content is coming from $1",
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
@ -3733,9 +3711,6 @@
"snapsSettingsDescription": {
"message": "Manage your Snaps"
},
"snapsStatus": {
"message": "Snap status is dependent on activity."
},
"snapsToggle": {
"message": "A snap will only run if it is enabled"
},
@ -4883,6 +4858,10 @@
"message": "You've added all the popular networks. You can discover more networks $1 Or you can $2",
"description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'"
},
"youInstalled": {
"message": "You installed",
"description": "Part of version description for installed snap"
},
"youNeedToAllowCameraAccess": {
"message": "You need to allow camera access to use this feature."
},

View File

@ -695,10 +695,6 @@
"message": "$1 no está conectado a ningún sitio.",
"description": "$1 is the account name"
},
"connectedSnapSites": {
"message": "El complemento de $1 está conectado a estos sitios. Tienen acceso a los permisos enumerados anteriormente.",
"description": "$1 represents the name of the snap"
},
"connecting": {
"message": "Estableciendo conexión…"
},
@ -1355,18 +1351,6 @@
"message": "¿No funciona la importación del archivo? Haga clic aquí.",
"description": "Helps user import their account from a JSON file"
},
"flaskSnapSettingsCardButtonCta": {
"message": "Ver detalles",
"description": "Call to action a user can take to see more information about the snap that is installed"
},
"flaskSnapSettingsCardDateAddedOn": {
"message": "Añadido el",
"description": "Start of the sentence describing when and where snap was added"
},
"flaskSnapSettingsCardFrom": {
"message": "de",
"description": "Part of the sentence describing when and where snap was added"
},
"flaskWelcomeUninstall": {
"message": "le recomendamos que desinstale esta extensión",
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
@ -3230,10 +3214,6 @@
"settingsSearchMatchingNotFound": {
"message": "No se encontraron resultados coincidentes."
},
"shorthandVersion": {
"message": "v$1",
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
},
"show": {
"message": "Mostrar"
},
@ -3307,14 +3287,6 @@
"smartTransaction": {
"message": "Transacción inteligente"
},
"snapAccess": {
"message": "El complemento de $1 tiene acceso a:",
"description": "$1 represents the name of the snap"
},
"snapAdded": {
"message": "Se agregó en $1 de $2",
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
},
"snapContent": {
"message": "Este contenido proviene de $1",
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
@ -3351,9 +3323,6 @@
"snapsSettingsDescription": {
"message": "Administre sus complementos"
},
"snapsStatus": {
"message": "El estado del complemento depende de la actividad."
},
"snapsToggle": {
"message": "Un complemento solo se ejecutará si está habilitado"
},

View File

@ -882,18 +882,6 @@
"message": "¿No funciona la importación del archivo? ¡Haga clic aquí!",
"description": "Helps user import their account from a JSON file"
},
"flaskSnapSettingsCardButtonCta": {
"message": "Ver detalles",
"description": "Call to action a user can take to see more information about the snap that is installed"
},
"flaskSnapSettingsCardDateAddedOn": {
"message": "Añadido el",
"description": "Start of the sentence describing when and where snap was added"
},
"flaskSnapSettingsCardFrom": {
"message": "de",
"description": "Part of the sentence describing when and where snap was added"
},
"flaskWelcomeUninstall": {
"message": "le recomendamos que desinstale esta extensión",
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."

View File

@ -695,10 +695,6 @@
"message": "$1 nest connecté à aucun site.",
"description": "$1 is the account name"
},
"connectedSnapSites": {
"message": "Le snap $1 est connecté à ces sites. Ils ont accès aux autorisations énumérées ci-dessus.",
"description": "$1 represents the name of the snap"
},
"connecting": {
"message": "Connexion…"
},
@ -1355,18 +1351,6 @@
"message": "Limportation de fichier ne fonctionne pas ? Cliquez ici !",
"description": "Helps user import their account from a JSON file"
},
"flaskSnapSettingsCardButtonCta": {
"message": "Voir les détails",
"description": "Call to action a user can take to see more information about the snap that is installed"
},
"flaskSnapSettingsCardDateAddedOn": {
"message": "Ajouté le",
"description": "Start of the sentence describing when and where snap was added"
},
"flaskSnapSettingsCardFrom": {
"message": "de",
"description": "Part of the sentence describing when and where snap was added"
},
"flaskWelcomeUninstall": {
"message": "vous devriez désinstaller cette extension",
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
@ -3230,10 +3214,6 @@
"settingsSearchMatchingNotFound": {
"message": "Aucun résultat correspondant trouvé."
},
"shorthandVersion": {
"message": "v$1",
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
},
"show": {
"message": "Afficher"
},
@ -3307,14 +3287,6 @@
"smartTransaction": {
"message": "Transaction intelligente"
},
"snapAccess": {
"message": "Le snap $1 peut accéder à :",
"description": "$1 represents the name of the snap"
},
"snapAdded": {
"message": "Ajouté le $1 à partir de $2",
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
},
"snapContent": {
"message": "Ce contenu provient de $1",
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
@ -3351,9 +3323,6 @@
"snapsSettingsDescription": {
"message": "Gérez vos Snaps"
},
"snapsStatus": {
"message": "Létat du Snap dépend de lactivité."
},
"snapsToggle": {
"message": "Un snap ne sexécute que sil est activé"
},

View File

@ -695,10 +695,6 @@
"message": "$1 किसी भी साइट से कनेक्ट नहीं है।",
"description": "$1 is the account name"
},
"connectedSnapSites": {
"message": "$1 स्नैप इन साइटों से जुड़ा है। वे ऊपर सूचीबद्ध अनुमतियों को एक्सेस कर सकती हैं।",
"description": "$1 represents the name of the snap"
},
"connecting": {
"message": "कनेक्ट किया जा रहा है..."
},
@ -1355,18 +1351,6 @@
"message": "फाइल आयात काम नहीं कर रहा है? यहां क्लिक करें!",
"description": "Helps user import their account from a JSON file"
},
"flaskSnapSettingsCardButtonCta": {
"message": "विवरण देखें",
"description": "Call to action a user can take to see more information about the snap that is installed"
},
"flaskSnapSettingsCardDateAddedOn": {
"message": "जोड़ा गया",
"description": "Start of the sentence describing when and where snap was added"
},
"flaskSnapSettingsCardFrom": {
"message": "से",
"description": "Part of the sentence describing when and where snap was added"
},
"flaskWelcomeUninstall": {
"message": "आपको इस एक्सटेन्शन को अनइंस्टाल करना चाहिए",
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
@ -3230,10 +3214,6 @@
"settingsSearchMatchingNotFound": {
"message": "कोई मेल खाने वाला परिणाम नहीं मिला।"
},
"shorthandVersion": {
"message": "v$1",
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
},
"show": {
"message": "दिखाएं"
},
@ -3307,14 +3287,6 @@
"smartTransaction": {
"message": "स्मार्ट लेनदेन"
},
"snapAccess": {
"message": "$1 स्नैप को एक्सेस है:",
"description": "$1 represents the name of the snap"
},
"snapAdded": {
"message": "$2 से $1 जोड़ा गया",
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
},
"snapContent": {
"message": "यह सामग्री $1 से आ रही है",
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
@ -3351,9 +3323,6 @@
"snapsSettingsDescription": {
"message": "अपने स्नैप्स प्रबंधित करें"
},
"snapsStatus": {
"message": "स्नैप स्टेटस एक्टिविटी पर निर्भर करता है।"
},
"snapsToggle": {
"message": "कोई स्नैप तभी चलेगा जब उसे सक्षम किया गया हो"
},

View File

@ -695,10 +695,6 @@
"message": "$1 tidak terhubung ke situs mana pun.",
"description": "$1 is the account name"
},
"connectedSnapSites": {
"message": "Snap $1 terhubung ke situs-situs ini. Token ini memiliki akses ke izin yang tercantum di atas. \t",
"description": "$1 represents the name of the snap"
},
"connecting": {
"message": "Menghubungkan..."
},
@ -1355,18 +1351,6 @@
"message": "Impor file tidak bekerja? Klik di sini!",
"description": "Helps user import their account from a JSON file"
},
"flaskSnapSettingsCardButtonCta": {
"message": "Lihat detailnya",
"description": "Call to action a user can take to see more information about the snap that is installed"
},
"flaskSnapSettingsCardDateAddedOn": {
"message": "Ditambahkan di",
"description": "Start of the sentence describing when and where snap was added"
},
"flaskSnapSettingsCardFrom": {
"message": "dari",
"description": "Part of the sentence describing when and where snap was added"
},
"flaskWelcomeUninstall": {
"message": "Anda harus menghapus ekstensi ini",
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
@ -3230,10 +3214,6 @@
"settingsSearchMatchingNotFound": {
"message": "Tidak menemukan hasil yang cocok."
},
"shorthandVersion": {
"message": "v$1",
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
},
"show": {
"message": "Tampil"
},
@ -3307,14 +3287,6 @@
"smartTransaction": {
"message": "Transaksi pintar"
},
"snapAccess": {
"message": "Snap $1 memiliki akses ke:",
"description": "$1 represents the name of the snap"
},
"snapAdded": {
"message": "Ditambahkan pada $1 dari $2",
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
},
"snapContent": {
"message": "Konten ini berasal dari $1",
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
@ -3351,9 +3323,6 @@
"snapsSettingsDescription": {
"message": "Kelola Snap Anda"
},
"snapsStatus": {
"message": "Status snap tergantung pada aktivitas."
},
"snapsToggle": {
"message": "Snap hanya akan beroperasi jika diaktifkan"
},

View File

@ -538,10 +538,6 @@
"message": "$1 non è connesso ad alcun sito.",
"description": "$1 is the account name"
},
"connectedSnapSites": {
"message": "$1 snap è collegato a questi siti. Hanno accesso alle autorizzazioni sopra elencate.",
"description": "$1 represents the name of the snap"
},
"connecting": {
"message": "Connessione..."
},

View File

@ -695,10 +695,6 @@
"message": "$1はどのサイトとも接続されていません。",
"description": "$1 is the account name"
},
"connectedSnapSites": {
"message": "$1 スナップはこれらのサイトに接続されており、上記のパーミッションにアクセスできます。",
"description": "$1 represents the name of the snap"
},
"connecting": {
"message": "接続中..."
},
@ -1355,18 +1351,6 @@
"message": "ファイルのインポートが機能していない場合ここをクリックしてください!",
"description": "Helps user import their account from a JSON file"
},
"flaskSnapSettingsCardButtonCta": {
"message": "詳細を表示",
"description": "Call to action a user can take to see more information about the snap that is installed"
},
"flaskSnapSettingsCardDateAddedOn": {
"message": "追加日",
"description": "Start of the sentence describing when and where snap was added"
},
"flaskSnapSettingsCardFrom": {
"message": "元",
"description": "Part of the sentence describing when and where snap was added"
},
"flaskWelcomeUninstall": {
"message": "この拡張機能はアンインストールしてください",
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
@ -3230,10 +3214,6 @@
"settingsSearchMatchingNotFound": {
"message": "一致する結果が見つかりませんでした。"
},
"shorthandVersion": {
"message": "v$1",
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
},
"show": {
"message": "表示"
},
@ -3307,14 +3287,6 @@
"smartTransaction": {
"message": "スマートトランザクション"
},
"snapAccess": {
"message": "$1 スナップは次にアクセス可能です:",
"description": "$1 represents the name of the snap"
},
"snapAdded": {
"message": "$1 に $2 から追加",
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
},
"snapContent": {
"message": "このコンテンツは $1 からのものです",
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
@ -3351,9 +3323,6 @@
"snapsSettingsDescription": {
"message": "スナップの管理"
},
"snapsStatus": {
"message": "スナップのステータスはアクティビティによります。"
},
"snapsToggle": {
"message": "スナップは有効になっている場合にのみ実行されます"
},

View File

@ -695,10 +695,6 @@
"message": "$1 계정은 어떤 사이트에도 연결되어 있지 않습니다.",
"description": "$1 is the account name"
},
"connectedSnapSites": {
"message": "$1 스냅이 이 사이트에 연결되어 있습니다. 이 사이트는 위에 나열된 접근 권한이 있습니다.",
"description": "$1 represents the name of the snap"
},
"connecting": {
"message": "연결 중..."
},
@ -1355,18 +1351,6 @@
"message": "파일 가져오기가 작동하지 않나요? 여기를 클릭하세요.",
"description": "Helps user import their account from a JSON file"
},
"flaskSnapSettingsCardButtonCta": {
"message": "세부 정보 보기",
"description": "Call to action a user can take to see more information about the snap that is installed"
},
"flaskSnapSettingsCardDateAddedOn": {
"message": "추가하기",
"description": "Start of the sentence describing when and where snap was added"
},
"flaskSnapSettingsCardFrom": {
"message": "발신",
"description": "Part of the sentence describing when and where snap was added"
},
"flaskWelcomeUninstall": {
"message": "이 확장 프로그램을 삭제해야 합니다",
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
@ -3230,10 +3214,6 @@
"settingsSearchMatchingNotFound": {
"message": "검색 결과가 없습니다."
},
"shorthandVersion": {
"message": "v$1",
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
},
"show": {
"message": "보기"
},
@ -3307,14 +3287,6 @@
"smartTransaction": {
"message": "스마트 트랜잭션"
},
"snapAccess": {
"message": "$1 스냅이 접근할 수 있는 대상:",
"description": "$1 represents the name of the snap"
},
"snapAdded": {
"message": "$1에 $2에서 추가됨",
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
},
"snapContent": {
"message": "콘텐츠 출처: $1",
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
@ -3351,9 +3323,6 @@
"snapsSettingsDescription": {
"message": "스냅 관리"
},
"snapsStatus": {
"message": "스냅 상태는 활동에 따라 달라집니다."
},
"snapsToggle": {
"message": "스냅은 활성화된 상태에서만 작동합니다."
},

View File

@ -695,10 +695,6 @@
"message": "$1 não está conectada a nenhum site.",
"description": "$1 is the account name"
},
"connectedSnapSites": {
"message": "O snap $1 está conectado a estes sites. Eles têm acesso às permissões listadas acima.",
"description": "$1 represents the name of the snap"
},
"connecting": {
"message": "Conectando..."
},
@ -1355,18 +1351,6 @@
"message": "A importação de ficheiro não está a funcionar? Carregue aqui!",
"description": "Helps user import their account from a JSON file"
},
"flaskSnapSettingsCardButtonCta": {
"message": "Ver detalhes",
"description": "Call to action a user can take to see more information about the snap that is installed"
},
"flaskSnapSettingsCardDateAddedOn": {
"message": "Adicionado em",
"description": "Start of the sentence describing when and where snap was added"
},
"flaskSnapSettingsCardFrom": {
"message": "de",
"description": "Part of the sentence describing when and where snap was added"
},
"flaskWelcomeUninstall": {
"message": "você deve desinstalar essa extensão",
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
@ -3230,10 +3214,6 @@
"settingsSearchMatchingNotFound": {
"message": "Nenhum resultado correspondente encontrado."
},
"shorthandVersion": {
"message": "v$1",
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
},
"show": {
"message": "Exibir"
},
@ -3307,14 +3287,6 @@
"smartTransaction": {
"message": "Transação inteligente"
},
"snapAccess": {
"message": "Snap $1 tem acesso a:",
"description": "$1 represents the name of the snap"
},
"snapAdded": {
"message": "Adicionado em $1 a partir de $2",
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
},
"snapContent": {
"message": "Esse conteúdo vem de $1",
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
@ -3351,9 +3323,6 @@
"snapsSettingsDescription": {
"message": "Gerencie seus snaps"
},
"snapsStatus": {
"message": "O status do snap depende da atividade."
},
"snapsToggle": {
"message": "O snap só será executado se estiver ativado"
},

View File

@ -882,18 +882,6 @@
"message": "A importação de arquivo não está funcionando? Clique aqui!",
"description": "Helps user import their account from a JSON file"
},
"flaskSnapSettingsCardButtonCta": {
"message": "Ver detalhes",
"description": "Call to action a user can take to see more information about the snap that is installed"
},
"flaskSnapSettingsCardDateAddedOn": {
"message": "Adicionado em",
"description": "Start of the sentence describing when and where snap was added"
},
"flaskSnapSettingsCardFrom": {
"message": "de",
"description": "Part of the sentence describing when and where snap was added"
},
"flaskWelcomeUninstall": {
"message": "você deve desinstalar essa extensão",
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."

View File

@ -695,10 +695,6 @@
"message": "$1 не подключен ни к каким сайтам.",
"description": "$1 is the account name"
},
"connectedSnapSites": {
"message": "Снап $1 подключен к этим сайтам. У них есть доступ к перечисленным выше разрешениям.",
"description": "$1 represents the name of the snap"
},
"connecting": {
"message": "Подключение..."
},
@ -1355,18 +1351,6 @@
"message": "Импорт файлов не работает? Нажмите здесь!",
"description": "Helps user import their account from a JSON file"
},
"flaskSnapSettingsCardButtonCta": {
"message": "См. подробности",
"description": "Call to action a user can take to see more information about the snap that is installed"
},
"flaskSnapSettingsCardDateAddedOn": {
"message": "Добавлена",
"description": "Start of the sentence describing when and where snap was added"
},
"flaskSnapSettingsCardFrom": {
"message": "от",
"description": "Part of the sentence describing when and where snap was added"
},
"flaskWelcomeUninstall": {
"message": "вам нужно должны удалить это расширение",
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
@ -3230,10 +3214,6 @@
"settingsSearchMatchingNotFound": {
"message": "Совпадений не найдено."
},
"shorthandVersion": {
"message": "v$1",
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
},
"show": {
"message": "Показать"
},
@ -3307,14 +3287,6 @@
"smartTransaction": {
"message": "Смарт-транзакция"
},
"snapAccess": {
"message": "У снапа $1 есть доступ к:",
"description": "$1 represents the name of the snap"
},
"snapAdded": {
"message": "Добавлено на $1 из $2",
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
},
"snapContent": {
"message": "Этот контент поступает от $1",
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
@ -3351,9 +3323,6 @@
"snapsSettingsDescription": {
"message": "Управление вашим снапами"
},
"snapsStatus": {
"message": "Статус снапа зависит от активности."
},
"snapsToggle": {
"message": "Снап будет работать только в том случае, если он включен"
},

View File

@ -695,10 +695,6 @@
"message": "Ang $1 ay hindi nakakonekta sa anumang site.",
"description": "$1 is the account name"
},
"connectedSnapSites": {
"message": "Ang $1 snap ay konektado sa mga site na ito. May access sila sa mga pahintulot na nakalista sa itaas.",
"description": "$1 represents the name of the snap"
},
"connecting": {
"message": "Kumokonekta..."
},
@ -1355,18 +1351,6 @@
"message": "Hindi gumagana ang pag-import ng file? Mag-click dito!",
"description": "Helps user import their account from a JSON file"
},
"flaskSnapSettingsCardButtonCta": {
"message": "Tingnan ang mga detalye",
"description": "Call to action a user can take to see more information about the snap that is installed"
},
"flaskSnapSettingsCardDateAddedOn": {
"message": "Dinagdag sa",
"description": "Start of the sentence describing when and where snap was added"
},
"flaskSnapSettingsCardFrom": {
"message": "mula sa",
"description": "Part of the sentence describing when and where snap was added"
},
"flaskWelcomeUninstall": {
"message": "dapat mong i-uninstall ang extension na ito",
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
@ -3230,10 +3214,6 @@
"settingsSearchMatchingNotFound": {
"message": "Walang nakitang katugmang resulta."
},
"shorthandVersion": {
"message": "v$1",
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
},
"show": {
"message": "Ipakita"
},
@ -3307,14 +3287,6 @@
"smartTransaction": {
"message": "Smart Transaction"
},
"snapAccess": {
"message": "Ang $1 snap ay may access sa:",
"description": "$1 represents the name of the snap"
},
"snapAdded": {
"message": "Idinagdag noong $1 mula sa $2",
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
},
"snapContent": {
"message": "Ang nilalamang ito ay nagmumula sa $1",
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
@ -3351,9 +3323,6 @@
"snapsSettingsDescription": {
"message": "Pamahalaan ang iyong mga Snap"
},
"snapsStatus": {
"message": "Ang lagay ng Snap ay nakadepende sa aktibidad."
},
"snapsToggle": {
"message": "Tatakbo lamang ang snap kapag pinagana ito"
},

View File

@ -695,10 +695,6 @@
"message": "$1 herhangi bir siteye bağlanmamış.",
"description": "$1 is the account name"
},
"connectedSnapSites": {
"message": "$1 snap bu sitelere bağlı. Yukarıda listelenen izinlere erişimleri vardır.",
"description": "$1 represents the name of the snap"
},
"connecting": {
"message": "Bağlanıyor..."
},
@ -1355,18 +1351,6 @@
"message": "Dosya içe aktarma çalışmıyor mu? Buraya tıklayın!",
"description": "Helps user import their account from a JSON file"
},
"flaskSnapSettingsCardButtonCta": {
"message": "Ayrıntıları gör",
"description": "Call to action a user can take to see more information about the snap that is installed"
},
"flaskSnapSettingsCardDateAddedOn": {
"message": "Şu tarihte eklendi:",
"description": "Start of the sentence describing when and where snap was added"
},
"flaskSnapSettingsCardFrom": {
"message": "şurada:",
"description": "Part of the sentence describing when and where snap was added"
},
"flaskWelcomeUninstall": {
"message": "bu uzantıyı kaldırmalısın",
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
@ -3230,10 +3214,6 @@
"settingsSearchMatchingNotFound": {
"message": "Eşleşen sonuç bulunamadı."
},
"shorthandVersion": {
"message": "s$1",
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
},
"show": {
"message": "Göster"
},
@ -3307,14 +3287,6 @@
"smartTransaction": {
"message": "Akıllı işlem"
},
"snapAccess": {
"message": "$1 snap'in şunlara erişimi vardır:",
"description": "$1 represents the name of the snap"
},
"snapAdded": {
"message": "$1 tarihinde $2 alanından eklendi",
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
},
"snapContent": {
"message": "Bu içerik $1 kaynaklıdır",
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
@ -3351,9 +3323,6 @@
"snapsSettingsDescription": {
"message": "Snap'lerini yönet"
},
"snapsStatus": {
"message": "Snap durumu etkinliğe bağlıdır."
},
"snapsToggle": {
"message": "Bir snap yalnızca etkinleştirilmişse çalışır"
},

View File

@ -695,10 +695,6 @@
"message": "$1 chưa được kết nối với bất kỳ trang web nào.",
"description": "$1 is the account name"
},
"connectedSnapSites": {
"message": "Snap $1 được kết nối với các trang web này. Các trang web này được phép sử dụng những quyền được liệt kê ở trên.",
"description": "$1 represents the name of the snap"
},
"connecting": {
"message": "Đang kết nối..."
},
@ -1355,18 +1351,6 @@
"message": "Tính năng nhập tập tin không hoạt động? Nhấn vào đây!",
"description": "Helps user import their account from a JSON file"
},
"flaskSnapSettingsCardButtonCta": {
"message": "Xem chi tiết",
"description": "Call to action a user can take to see more information about the snap that is installed"
},
"flaskSnapSettingsCardDateAddedOn": {
"message": "Đã thêm vào",
"description": "Start of the sentence describing when and where snap was added"
},
"flaskSnapSettingsCardFrom": {
"message": "từ",
"description": "Part of the sentence describing when and where snap was added"
},
"flaskWelcomeUninstall": {
"message": "bạn nên gỡ cài đặt tiện ích mở rộng này",
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
@ -3230,10 +3214,6 @@
"settingsSearchMatchingNotFound": {
"message": "Không tìm thấy kết quả trùng khớp."
},
"shorthandVersion": {
"message": "v$1",
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
},
"show": {
"message": "Hiển thị"
},
@ -3307,14 +3287,6 @@
"smartTransaction": {
"message": "Giao dịch thông minh"
},
"snapAccess": {
"message": "Snap $1 có quyền truy cập vào:",
"description": "$1 represents the name of the snap"
},
"snapAdded": {
"message": "Đã thêm vào $1 từ $2",
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
},
"snapContent": {
"message": "Nội dung này đến từ $1",
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
@ -3351,9 +3323,6 @@
"snapsSettingsDescription": {
"message": "Quản lý Snap"
},
"snapsStatus": {
"message": "Trạng thái Snap tùy thuộc vào hoạt động."
},
"snapsToggle": {
"message": "Snap chỉ hoạt động khi đã bật"
},

View File

@ -695,10 +695,6 @@
"message": "$1 还没连接到任何网站。",
"description": "$1 is the account name"
},
"connectedSnapSites": {
"message": "$1的snap已连接到这些站点。它们有上述的访问权限。",
"description": "$1 represents the name of the snap"
},
"connecting": {
"message": "连接中……"
},
@ -1355,18 +1351,6 @@
"message": "文件导入失败?点击这里!",
"description": "Helps user import their account from a JSON file"
},
"flaskSnapSettingsCardButtonCta": {
"message": "查看详细信息",
"description": "Call to action a user can take to see more information about the snap that is installed"
},
"flaskSnapSettingsCardDateAddedOn": {
"message": "添加于",
"description": "Start of the sentence describing when and where snap was added"
},
"flaskSnapSettingsCardFrom": {
"message": "自",
"description": "Part of the sentence describing when and where snap was added"
},
"flaskWelcomeUninstall": {
"message": "您应该卸载此扩展程序",
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
@ -3230,10 +3214,6 @@
"settingsSearchMatchingNotFound": {
"message": "没有找到匹配的结果."
},
"shorthandVersion": {
"message": "v$1",
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
},
"show": {
"message": "显示"
},
@ -3307,14 +3287,6 @@
"smartTransaction": {
"message": "智能交易"
},
"snapAccess": {
"message": "$1的snap可以访问",
"description": "$1 represents the name of the snap"
},
"snapAdded": {
"message": "从 $2 添加到 $1",
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
},
"snapContent": {
"message": "此内容来自$1",
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
@ -3351,9 +3323,6 @@
"snapsSettingsDescription": {
"message": "管理您的Snap"
},
"snapsStatus": {
"message": "Snap状态取决于活动。"
},
"snapsToggle": {
"message": "Snap仅在启用后才会运行"
},

View File

@ -57,7 +57,43 @@
"priorityFeeTrend": "down",
"networkCongestion": 0.90625
},
"snaps": [{}],
"snaps": {
"npm:@metamask/test-snap-bip44": {
"id": "npm:@metamask/test-snap-bip44",
"origin": "npm:@metamask/test-snap-bip44",
"version": "5.1.2",
"iconUrl": null,
"initialPermissions": {
"endowment:ethereum-provider": {}
},
"manifest": {
"description": "An example Snap that signs messages using BLS.",
"proposedName": "BIP-44 Test Snap",
"repository": {
"type": "git",
"url": "https://github.com/MetaMask/test-snaps.git"
},
"source": {
"location": {
"npm": {
"filePath": "dist/bundle.js",
"packageName": "@metamask/test-snap-bip44",
"registry": "https://registry.npmjs.org"
}
},
"shasum": "L1k+dT9Q+y3KfIqzaH09MpDZVPS9ZowEh9w01ZMTWMU="
},
"version": "5.1.2"
},
"versionHistory": [
{
"date": 1680686075921,
"origin": "https://metamask.github.io",
"version": "5.1.2"
}
]
}
},
"preferences": {
"hideZeroBalanceTokens": false,
"showFiatInTestnets": false,

View File

@ -92,6 +92,10 @@ describe('Test Snap Management', function () {
await driver.delay(1000);
// try to disable the snap
await driver.clickElement({
text: 'Notification Test Snap',
tag: 'p',
});
await driver.clickElement('.toggle-button > div');
// switch back to test-snaps window
@ -130,12 +134,11 @@ describe('Test Snap Management', function () {
);
assert.equal(await notificationResult.getText(), '1');
// click on see details
await driver.clickElement({ text: 'See details', tag: 'button' });
await driver.delay(1000);
// try to remove snap
await driver.clickElement({ text: 'Remove snap', tag: 'button' });
await driver.clickElement({
text: 'Remove Notification Test Snap',
tag: 'p',
});
await driver.delay(1000);
// try to click remove on popover

View File

@ -50,7 +50,7 @@ const SnapAvatar = ({ snapId, className }) => {
position={BadgeWrapperPosition.bottomRight}
>
{iconUrl ? (
<AvatarFavicon size={Size.LG} src={iconUrl} />
<AvatarFavicon size={Size.LG} src={iconUrl} name={friendlyName} />
) : (
<AvatarBase
size={Size.LG}

View File

@ -1,57 +1,9 @@
$version-max-width: 56px; // Increase to show more of the version number
$body-line-clamp: 4; // Number of lines in card body before truncating
.snap-settings-card {
&__title {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
&__inner-wrapper {
cursor: pointer;
}
&__toggle-container {
margin-left: auto;
&__toggle-button {
margin-right: -12px; // react-toggle-button width fix
}
}
&__body {
overflow: hidden;
/* stylelint-disable */
display: -webkit-box;
-webkit-line-clamp: $body-line-clamp;
-webkit-box-orient: vertical;
/* stylelint-enable */
}
&__version.box {
margin-left: auto;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
min-width: $version-max-width;
flex: 0 0 $version-max-width;
text-align: right;
}
&__chip.chip {
margin: 0;
margin-left: auto;
display: inline-flex;
align-items: center;
text-transform: capitalize;
}
&__button.button {
padding: 4px 16px;
display: inline-flex;
align-items: center;
}
&__date-added {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
&__caret {
cursor: pointer;
}
}

View File

@ -1,194 +1,66 @@
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { useI18nContext } from '../../../../hooks/useI18nContext';
import Card from '../../../ui/card';
import Box from '../../../ui/box';
import IconWithFallback from '../../../ui/icon-with-fallback';
import IconBorder from '../../../ui/icon-border';
import Typography from '../../../ui/typography/typography';
import ToggleButton from '../../../ui/toggle-button';
import Chip from '../../../ui/chip';
import ColorIndicator from '../../../ui/color-indicator';
import Button from '../../../ui/button';
import Tooltip from '../../../ui/tooltip';
import {
Color,
TypographyVariant,
FONT_WEIGHT,
AlignItems,
JustifyContent,
DISPLAY,
TEXT_ALIGN,
TextColor,
BackgroundColor,
BLOCK_SIZES,
IconColor,
TextVariant,
} from '../../../../helpers/constants/design-system';
import { Icon, IconName, IconSize, Text } from '../../../component-library';
import SnapAvatar from '../snap-avatar';
const STATUSES = {
INSTALLING: 'installing',
RUNNING: 'running',
STOPPED: 'stopped',
CRASHED: 'crashed',
};
const STATUS_COLORS = {
[STATUSES.INSTALLING]: Color.warningDefault,
[STATUSES.RUNNING]: Color.successDefault,
[STATUSES.STOPPED]: Color.infoMuted,
[STATUSES.CRASHED]: Color.errorDefault,
};
const SnapSettingsCard = ({
name,
description,
icon,
dateAdded = '',
version,
url,
onToggle,
isEnabled = false,
onClick,
status,
className,
cardProps,
toggleButtonProps,
buttonProps,
chipProps,
}) => {
const t = useI18nContext();
const SnapSettingsCard = ({ name, packageName, onClick, snapId }) => {
return (
<Card
className={classnames('snap-settings-card', className)}
{...cardProps}
<Box
className="snap-settings-card"
display={DISPLAY.FLEX}
alignItems={AlignItems.center}
justifyContent={JustifyContent.spaceBetween}
width={BLOCK_SIZES.FULL}
padding={[4, 4, 4, 4]}
>
<Box
className="snap-settings-card__inner-wrapper"
display={DISPLAY.FLEX}
alignItems={AlignItems.center}
marginBottom={4}
justifyContent={JustifyContent.flexStart}
width={BLOCK_SIZES.FULL}
onClick={onClick}
>
{(icon || name) && (
<Box>
<IconBorder size={32}>
<IconWithFallback icon={icon} size={32} name={name} />
</IconBorder>
</Box>
)}
<Typography
boxProps={{
marginLeft: 4,
marginTop: 0,
marginBottom: 0,
}}
color={TextColor.textDefault}
variant={TypographyVariant.H4}
fontWeight={FONT_WEIGHT.BOLD}
className="snap-settings-card__title"
>
{name}
</Typography>
<Box paddingLeft={4} className="snap-settings-card__toggle-container">
<Tooltip interactive position="bottom" html={t('snapsToggle')}>
<ToggleButton
value={isEnabled}
onToggle={onToggle}
className="snap-settings-card__toggle-container__toggle-button"
{...toggleButtonProps}
/>
</Tooltip>
<Box>
<SnapAvatar snapId={snapId} />
</Box>
</Box>
<Typography
variant={TypographyVariant.paragraph}
color={TextColor.textAlternative}
fontWeight={FONT_WEIGHT.NORMAL}
className="snap-settings-card__body"
boxProps={{
marginBottom: 4,
marginTop: 0,
margin: [0, 0, 4],
}}
>
{description}
</Typography>
<Box>
<Box marginBottom={4}>
<Box
display={DISPLAY.FLEX}
alignItems={AlignItems.center}
justifyContent={JustifyContent.spaceBetween}
marginBottom={4}
<Box paddingLeft={4} paddingRight={4} width={BLOCK_SIZES.FULL}>
<Text
className="snap-settings-card__title"
color={Color.textDefault}
variant={TextVariant.bodyMd}
>
<Box>
<Button
className="snap-settings-card__button"
onClick={onClick}
{...buttonProps}
>
{t('flaskSnapSettingsCardButtonCta')}
</Button>
</Box>
<Tooltip interactive position="bottom" html={t('snapsStatus')}>
<Chip
leftIcon={
<Box paddingLeft={1}>
<ColorIndicator
color={STATUS_COLORS[status]}
type={ColorIndicator.TYPES.FILLED}
/>
</Box>
}
label={status}
labelProps={{
color: Color.textAlternative,
margin: [0, 1],
}}
backgroundColor={BackgroundColor.backgroundAlternative}
className="snap-settings-card__chip"
{...chipProps}
/>
</Tooltip>
</Box>
</Box>
<Box display={DISPLAY.FLEX} alignItems={AlignItems.center}>
{(dateAdded || version) && (
<>
<Typography
boxProps={{
margin: [0, 0],
}}
color={TextColor.textMuted}
variant={TypographyVariant.H8}
fontWeight={FONT_WEIGHT.NORMAL}
as="span"
className="snap-settings-card__date-added"
>
{`${
dateAdded && t('flaskSnapSettingsCardDateAddedOn')
} ${dateAdded} ${url && t('flaskSnapSettingsCardFrom')} ${url}`}
</Typography>
<Typography
boxProps={{
paddingLeft: 2,
margin: [0, 0],
}}
color={TextColor.textMuted}
variant={TypographyVariant.H7}
fontWeight={FONT_WEIGHT.NORMAL}
align={TEXT_ALIGN.CENTER}
as="span"
className="snap-settings-card__version"
>
{t('shorthandVersion', [version])}
</Typography>
</>
)}
{name}
</Text>
<Text
className="snap-settings-card__url"
color={Color.textAlternative}
variant={TextVariant.bodySm}
>
{packageName}
</Text>
</Box>
</Box>
</Card>
<Box className="snap-settings-card__caret" onClick={onClick}>
<Icon
name={IconName.ArrowRight}
size={IconSize.Md}
color={IconColor.iconMuted}
/>
</Box>
</Box>
);
};
@ -198,61 +70,17 @@ SnapSettingsCard.propTypes = {
*/
name: PropTypes.string,
/**
* Description of the snap. Truncates after 4 lines
* Name of a snap package
*/
description: PropTypes.string,
/**
* Image source of the snap icon for the IconWithFallback component
*/
icon: PropTypes.string,
/**
* Date the snap was added. Date will need formatting
*/
dateAdded: PropTypes.string,
/**
* The version of the snap in semver. Will truncate after 4 numbers e.g. 10.5.1...
*/
version: PropTypes.string,
/**
* Url of the snap website
*/
url: PropTypes.string,
/**
* The onChange function for the ToggleButton component
*/
onToggle: PropTypes.func,
/**
* Whether the snap is enabled. `value` prop of the ToggleButton
*/
isEnabled: PropTypes.bool,
packageName: PropTypes.string,
/**
* onClick function of the "See Details" Button
*/
onClick: PropTypes.func,
/**
* Status of the snap must be one
* ID of a snap.
*/
status: PropTypes.oneOf(Object.values(STATUSES)).isRequired,
/**
* Additional className added to the root div of the SnapSettingsCard component
*/
className: PropTypes.string,
/**
* Optional additional props passed to the Card component
*/
cardProps: PropTypes.shape(Card.propTypes),
/**
* Optional additional props passed to the ToggleButton component
*/
toggleButtonProps: PropTypes.shape(ToggleButton.propTypes),
/**
* Optional additional props passed to the Button component
*/
buttonProps: PropTypes.shape(Button.propTypes),
/**
* Optional additional props passed to the Chip component
*/
chipProps: PropTypes.shape(Chip.propTypes),
snapId: PropTypes.string.isRequired,
};
export default SnapSettingsCard;

View File

@ -1,76 +1,46 @@
import * as React from 'react';
import { render, fireEvent } from '@testing-library/react';
import { screen } from '@testing-library/react';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { renderWithProvider } from '../../../../../test/lib/render-helpers';
import mockState from '../../../../../test/data/mock-state.json';
import SnapSettingsCard from '.';
describe('SnapSettingsCard', () => {
const args = {
name: 'Snap name',
description:
'This snap provides developers everywhere access to an entirely new data storage paradigm, even letting your programs store data autonomously.',
dateAdded: new Date().toDateString(),
version: '10.5.1234',
url: 'https://metamask.io',
status: 'stopped',
icon: './AST.png',
packageName: '@metamask/test-snap-bip44',
snapId: 'npm:@metamask/test-snap-bip44',
onClick: () => null,
};
const mockStore = configureMockStore([thunk])(mockState);
it('should render the SnapsSettingCard without crashing', () => {
const { getByText } = render(<SnapSettingsCard {...args} />);
const { getByText } = renderWithProvider(
<SnapSettingsCard {...args} />,
mockStore,
);
expect(getByText('Snap name')).toBeDefined();
});
it('should render the pill as installing when given a status of installing', () => {
args.status = 'installing';
const { getByText } = render(<SnapSettingsCard {...args} />);
expect(getByText('installing')).toBeDefined();
});
it('should render the pill as running when given a status of running', () => {
args.status = 'running';
const { getByText } = render(<SnapSettingsCard {...args} />);
expect(getByText('running')).toBeDefined();
});
it('should render the pill as installing when given a status of stopped', () => {
args.status = 'stopped';
const { getByText } = render(<SnapSettingsCard {...args} />);
expect(getByText('stopped')).toBeDefined();
});
it('should render the pill as crashed when given a status of crashed', () => {
args.status = 'crashed';
const { getByText } = render(<SnapSettingsCard {...args} />);
expect(getByText('crashed')).toBeDefined();
});
it('should call onToggle prop when toggle button is clicked', () => {
const onToggle = jest.fn();
args.onToggle = onToggle;
const { container } = render(<SnapSettingsCard {...args} />);
const toggleBtn = container.querySelector('.toggle-button').firstChild;
fireEvent.click(toggleBtn);
expect(onToggle).toHaveBeenCalled();
});
it('should call onClick prop when See Details button is clicked', () => {
const onClick = jest.fn();
args.onClick = onClick;
const { container } = render(<SnapSettingsCard {...args} />);
const seeDetailsBtn = container.querySelector(
'.snap-settings-card__button',
it('should render the icon fallback using the first letter of the name', async () => {
const { getByText } = renderWithProvider(
<SnapSettingsCard {...args} icon="" />,
mockStore,
);
fireEvent.click(seeDetailsBtn);
expect(onClick).toHaveBeenCalled();
const avatar = await screen.findAllByText(/B/u);
avatar.forEach((avatarBaseElement) => {
expect(avatarBaseElement).toHaveClass('mm-avatar-base');
});
expect(getByText('B')).toBeDefined();
});
it('should render an icon image', () => {
const { getByAltText } = render(<SnapSettingsCard {...args} />);
const image = getByAltText(args.name);
expect(image).toBeDefined();
expect(image).toHaveAttribute('src', args.icon);
});
it('should render the icon fallback using the first letter of the name', () => {
const { getByText } = render(<SnapSettingsCard {...args} icon="" />);
expect(getByText('S')).toBeDefined();
it('should render the package name', () => {
const { getByText } = renderWithProvider(
<SnapSettingsCard {...args} icon="" />,
mockStore,
);
expect(getByText('@metamask/test-snap-bip44')).toBeDefined();
});
});

View File

@ -29,7 +29,7 @@ export default {
key: 'contacts',
},
{
icon: <i className="fa fa-flask" />,
icon: <Icon name={IconName.Snaps} />,
content: 'Snaps',
key: 'snaps',
},

View File

@ -3,22 +3,15 @@
height: 100%;
&__wrapper {
width: auto;
max-width: 475px;
}
&__body {
padding: 12px 18px;
@include screen-sm-min {
padding: 12px;
}
}
.snap-settings-card {
margin: 8px 0;
max-width: 344px;
}
.snap-list-tab__container--no-snaps_inner {
max-width: 164px;
}

View File

@ -1,5 +1,5 @@
import React, { useRef, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useSelector } from 'react-redux';
import { useHistory } from 'react-router-dom';
import SnapSettingsCard from '../../../../components/app/flask/snap-settings-card';
import { useI18nContext } from '../../../../hooks/useI18nContext';
@ -13,8 +13,7 @@ import {
} from '../../../../helpers/constants/design-system';
import Box from '../../../../components/ui/box';
import { SNAPS_VIEW_ROUTE } from '../../../../helpers/constants/routes';
import { disableSnap, enableSnap } from '../../../../store/actions';
import { getSnaps } from '../../../../selectors';
import { getSnapsList } from '../../../../selectors';
import { handleSettingsRefs } from '../../../../helpers/utils/settings-search';
import {
Icon,
@ -31,46 +30,33 @@ import {
const SnapListTab = () => {
const t = useI18nContext();
const history = useHistory();
const dispatch = useDispatch();
const snaps = useSelector(getSnaps);
const settingsRef = useRef();
const onClick = (snap) => {
history.push(`${SNAPS_VIEW_ROUTE}/${encodeURIComponent(snap.id)}`);
};
const onToggle = (snap) => {
if (snap.enabled) {
dispatch(disableSnap(snap.id));
} else {
dispatch(enableSnap(snap.id));
}
};
useEffect(() => {
handleSettingsRefs(t, t('snaps'), settingsRef);
}, [settingsRef, t]);
const snapsList = useSelector((state) => getSnapsList(state));
return (
<div className="snap-list-tab" ref={settingsRef}>
{Object.entries(snaps).length ? (
{snapsList.length ? (
<div className="snap-list-tab__body">
<div className="snap-list-tab__wrapper">
{Object.entries(snaps).map(([key, snap]) => {
{snapsList.map((snap) => {
return (
<SnapSettingsCard
className="snap-settings-card"
isEnabled={snap.enabled}
key={key}
onToggle={() => {
onToggle(snap);
}}
description={snap.manifest.description}
url={snap.id}
name={snap.manifest.proposedName}
status={snap.status}
version={snap.version}
key={snap.key}
packageName={snap.packageName}
name={snap.name}
onClick={() => {
onClick(snap);
}}
snapId={snap.id}
/>
);
})}

View File

@ -1,131 +1,58 @@
.view-snap {
padding: 12px 18px;
max-width: 475px;
@include screen-sm-min {
padding: 12px;
}
&__version_info {
&__version-number {
font-weight: bold;
}
&__subheader {
padding: 16px 4px;
border-bottom: 1px solid var(--color-border-muted);
margin-inline-end: 24px;
height: 72px;
align-items: center;
display: flex;
flex-flow: row nowrap;
@include screen-sm-max {
margin-inline-end: 0;
padding: 0 0 16px;
flex-direction: column;
align-items: center;
gap: 8px;
height: max-content;
&__link {
vertical-align: top;
}
}
&__install-details {
border-bottom: 1px solid var(--color-border-muted);
margin-inline-end: 24px;
@include screen-sm-max {
margin-inline-end: 0;
&__enable {
&__tooltip_wrapper {
max-width: 52px;
}
}
&__version {
font-family: monospace;
}
&__title {
@include screen-sm-max {
padding-bottom: 16px;
}
}
&__pill-toggle-container {
align-items: center;
display: flex;
flex-grow: 1;
@include screen-sm-max {
width: 100%;
justify-content: space-between;
}
}
&__pill-container {
@include screen-sm-max {
padding-left: 0;
display: inline-block;
}
}
&__toggle-container {
margin-left: auto;
@include screen-sm-max {
padding-left: 0;
display: inline-block;
}
}
&__toggle-button {
margin-right: -12px;
}
&__content-container {
@include screen-sm-max {
width: 100%;
}
}
&__section {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
border-bottom: 1px solid var(--color-border-muted);
padding-bottom: 16px;
margin-bottom: 16px;
@include screen-sm-max {
height: initial;
padding: 5px 0 16px;
}
.connected-sites-list__content-row {
.connected-sites-list {
&__content-row {
border-top: none;
border-bottom: 1px solid var(--color-border-muted);
padding: 0;
&:last-child {
border-bottom: none;
& &-link-button {
padding: 0;
padding-inline-start: 0;
color: var(--color-error-default);
font-size: 14px;
}
a {
font-size: 14px;
color: var(--color-error-default);
}
a:hover {
color: var(--color-error-alternative);
}
}
&:last-child {
margin-bottom: 0;
border-bottom: none;
}
}
&__permission-list {
padding-bottom: 0;
.permission {
padding-top: 16px;
&:last-child {
border-bottom: none;
&__subject-info {
a.btn-link {
font-size: 14px;
color: var(--color-error-default);
}
}
}
&__remove-button {
max-width: 175px;
&__subject-icon {
flex-shrink: 0;
}
@include screen-sm-max {
align-self: center;
&__subject-name {
font-size: 14px;
color: var(--color-primary-default);
}
}
}

View File

@ -5,15 +5,13 @@ import {
SnapCaveatType,
WALLET_SNAP_PERMISSION_KEY,
} from '@metamask/rpc-methods';
import { getSnapPrefix } from '@metamask/snaps-utils';
import Button from '../../../../components/ui/button';
import Typography from '../../../../components/ui/typography';
import { useI18nContext } from '../../../../hooks/useI18nContext';
import {
TypographyVariant,
TEXT_ALIGN,
FRACTIONS,
Size,
TextColor,
BLOCK_SIZES,
TextVariant,
} from '../../../../helpers/constants/design-system';
import SnapAuthorship from '../../../../components/app/flask/snap-authorship';
import Box from '../../../../components/ui/box';
@ -36,7 +34,12 @@ import {
getPermissionSubjects,
getTargetSubjectMetadata,
} from '../../../../selectors';
import { formatDate } from '../../../../helpers/utils/util';
import {
formatDate,
getSnapName,
removeSnapIdPrefix,
} from '../../../../helpers/utils/util';
import { ButtonLink, Text } from '../../../../components/component-library';
import SnapPermissionsList from '../../../../components/app/flask/snap-permissions-list';
function ViewSnap() {
@ -108,140 +111,165 @@ function ViewSnap() {
}
const versionHistory = snap.versionHistory ?? [];
const [firstInstall] = versionHistory;
const installInfo = versionHistory.length
? versionHistory[versionHistory.length - 1]
: undefined;
const packageName = snap.id && removeSnapIdPrefix(snap.id);
const snapPrefix = snap.id && getSnapPrefix(snap.id);
const isNPM = snapPrefix === 'npm:';
const url = isNPM
? `https://www.npmjs.com/package/${packageName}`
: packageName;
const snapName = getSnapName(snap.id, targetSubjectMetadata);
return (
<div className="view-snap">
<div className="settings-page__content-row">
<div className="view-snap__subheader">
<Typography
className="view-snap__title"
variant={TypographyVariant.H3}
boxProps={{ textAlign: TEXT_ALIGN.CENTER }}
<Box
className="view-snap"
paddingBottom={8}
paddingLeft={3}
paddingRight={3}
>
<Box
className="view-snap__header"
paddingTop={8}
marginLeft={4}
marginRight={4}
>
<SnapAuthorship snapId={snap.id} />
</Box>
<Box
className="view-snap__description"
marginTop={4}
marginLeft={4}
marginRight={4}
>
<Text variant={TextVariant.bodyMd} color={TextColor.textDefault}>
{snap.manifest.description}
</Text>
</Box>
<Box
className="view-snap__version_info"
marginTop={2}
marginLeft={4}
marginRight={4}
>
<Text variant={TextVariant.bodyMd} color={TextColor.textDefault}>
{`${t('youInstalled')} `}
<span className="view-snap__version_info__version-number">
v{snap.version}
</span>
{` ${t('ofTextNofM')} `}
<ButtonLink
size={Size.auto}
href={url}
target="_blank"
className="view-snap__version_info__link"
>
{snap.manifest.proposedName}
</Typography>
<Box className="view-snap__pill-toggle-container">
<Box className="view-snap__pill-container" paddingLeft={2}>
<SnapAuthorship snapId={snap.id} />
</Box>
<Box paddingLeft={4} className="view-snap__toggle-container">
<Tooltip interactive position="bottom" html={t('snapsToggle')}>
<ToggleButton
value={snap.enabled}
onToggle={onToggle}
className="view-snap__toggle-button"
/>
</Tooltip>
</Box>
</Box>
</div>
<Box
className="view-snap__install-details"
display="flex"
justifyContent="space-between"
alignItems="center"
padding={2}
>
{firstInstall && (
<Typography variant={TypographyVariant.H8}>
{t('snapAdded', [
formatDate(firstInstall.date, 'MMMM d, y'),
firstInstall.origin,
])}
</Typography>
{packageName}
</ButtonLink>
{installInfo && ` ${t('from').toLowerCase()} `}
{installInfo && (
<ButtonLink
size={Size.auto}
href={installInfo.origin}
target="_blank"
className="view-snap__version_info__link"
>
{installInfo.origin}
</ButtonLink>
)}
<Typography
className="view-snap__version"
variant={TypographyVariant.H7}
>
{t('shorthandVersion', [snap.version])}
</Typography>
</Box>
<Box
className="view-snap__content-container"
width={FRACTIONS.SEVEN_TWELFTHS}
{installInfo &&
` ${t('on').toLowerCase()} ${formatDate(
installInfo.date,
'dd MMM yyyy',
)}`}
.
</Text>
</Box>
<Box
className="view-snap__enable"
marginTop={12}
marginLeft={4}
marginRight={4}
>
<Text variant={TextVariant.bodyLgMedium}>{t('enableSnap')}</Text>
<Text
variant={TextVariant.bodyMd}
color={TextColor.textDefault}
marginBottom={4}
>
<div className="view-snap__section">
<Typography
variant={TypographyVariant.H6}
color={TextColor.textAlternative}
boxProps={{ marginTop: 5 }}
>
{snap.manifest.description}
</Typography>
</div>
<div className="view-snap__section view-snap__permission-list">
<Typography variant={TypographyVariant.H4}>
{t('permissions')}
</Typography>
<Typography
variant={TypographyVariant.H6}
color={TextColor.textAlternative}
>
{t('snapAccess', [snap.manifest.proposedName])}
</Typography>
<Box width={BLOCK_SIZES.FULL}>
<SnapPermissionsList
permissions={permissions ?? {}}
targetSubjectMetadata={targetSubjectMetadata}
/>
</Box>
</div>
<div className="view-snap__section">
<Box width="11/12">
<Typography variant={TypographyVariant.H4}>
{t('connectedSites')}
</Typography>
<Typography
variant={TypographyVariant.H6}
color={TextColor.textAlternative}
>
{t('connectedSnapSites', [snap.manifest.proposedName])}
</Typography>
<ConnectedSitesList
connectedSubjects={connectedSubjects}
onDisconnect={(origin) => {
onDisconnect(origin, snap.id);
}}
/>
</Box>
</div>
<div className="view-snap__section">
<Typography variant={TypographyVariant.H4}>
{t('removeSnap')}
</Typography>
<Typography
variant={TypographyVariant.H6}
color={TextColor.textAlternative}
boxProps={{ paddingBottom: 3 }}
>
{t('removeSnapDescription')}
</Typography>
<Button
className="view-snap__remove-button"
type="danger"
css={{
maxWidth: '175px',
}}
onClick={() => setIsShowingRemoveWarning(true)}
>
{t('removeSnap')}
</Button>
{isShowingRemoveWarning && (
<SnapRemoveWarning
onCancel={() => setIsShowingRemoveWarning(false)}
onSubmit={async () => {
await dispatch(removeSnap(snap.id));
}}
snapName={snap.manifest.proposedName}
/>
)}
</div>
{t('enableSnapDescription')}
</Text>
<Box className="view-snap__enable__tooltip_wrapper">
<Tooltip interactive position="left" html={t('snapsToggle')}>
<ToggleButton
value={snap.enabled}
onToggle={onToggle}
className="view-snap__toggle-button"
/>
</Tooltip>
</Box>
</div>
</div>
</Box>
<Box className="view-snap__permissions" marginTop={12}>
<Text variant={TextVariant.bodyLgMedium} marginLeft={4} marginRight={4}>
{t('permissions')}
</Text>
<SnapPermissionsList
permissions={permissions ?? {}}
targetSubjectMetadata={targetSubjectMetadata}
/>
</Box>
<Box
className="view-snap__connected-sites"
marginTop={12}
marginLeft={4}
marginRight={4}
>
<Text variant={TextVariant.bodyLgMedium} marginBottom={4}>
{t('connectedSites')}
</Text>
<ConnectedSitesList
connectedSubjects={connectedSubjects}
onDisconnect={(origin) => {
onDisconnect(origin, snap.id);
}}
/>
</Box>
<Box
className="view-snap__remove"
marginTop={12}
marginLeft={4}
marginRight={4}
>
<Text variant={TextVariant.bodyLgMedium} color={TextColor.textDefault}>
{t('removeSnap')}
</Text>
<Text variant={TextVariant.bodyMd} color={TextColor.textDefault}>
{t('removeSnapDescription')}
</Text>
<Box marginTop={4}>
<Button
className="view-snap__remove-button"
type="danger"
onClick={() => setIsShowingRemoveWarning(true)}
>
<Text variant={TextVariant.bodyMd} color={TextColor.errorDefault}>
{`${t('remove')} ${snapName}`}
</Text>
</Button>
{isShowingRemoveWarning && (
<SnapRemoveWarning
onCancel={() => setIsShowingRemoveWarning(false)}
onSubmit={async () => {
await dispatch(removeSnap(snap.id));
}}
snapName={snapName}
/>
)}
</Box>
</Box>
</Box>
);
}
export default React.memo(ViewSnap);
export default ViewSnap;

View File

@ -0,0 +1,81 @@
import * as React from 'react';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { renderWithProvider } from '../../../../../test/lib/render-helpers';
import mockState from '../../../../../test/data/mock-state.json';
import ViewSnap from './view-snap';
jest.mock('../../../../store/actions.ts', () => {
return {
disableSnap: jest.fn(),
enableSnap: jest.fn(),
removeSnap: jest.fn(),
removePermissionsFor: jest.fn(),
updateCaveat: jest.fn(),
};
});
jest.mock('react-router-dom', () => {
const original = jest.requireActual('react-router-dom');
return {
...original,
useLocation: jest.fn(() => ({
pathname: `/settings/snaps-view/${encodeURIComponent(
'npm:@metamask/test-snap-bip44',
)}`,
})),
};
});
const mockStore = configureMockStore([thunk])(mockState);
describe('ViewSnap', () => {
it('should properly display Snap View elements', async () => {
const { getByText, container, getByRole } = renderWithProvider(
<ViewSnap />,
mockStore,
);
// Snap name & Snap authorship component
expect(getByText('BIP-44 Test Snap')).toBeDefined();
expect(container.getElementsByClassName('snaps-authorship')?.length).toBe(
1,
);
// Snap description
expect(
getByText('An example Snap that signs messages using BLS.'),
).toBeDefined();
// Snap version info
expect(getByText('v5.1.2')).toBeDefined();
// Enable Snap
expect(getByText('Enable snap')).toBeDefined();
expect(
getByText(
'Your installed snap will only have access to its permissions and run if its enabled.',
),
).toBeDefined();
expect(container.getElementsByClassName('toggle-button')?.length).toBe(1);
// Permissions
expect(getByText('Permissions')).toBeDefined();
expect(
container.getElementsByClassName('snap-permissions-list')?.length,
).toBe(1);
// Connected sites
expect(getByText('Connected sites')).toBeDefined();
expect(
container.getElementsByClassName('connected-sites-list__content-rows')
?.length,
).toBe(1);
// Remove snap
expect(getByText('Remove snap')).toBeDefined();
expect(
getByText(
'This action will delete the snap, its data and revoke your given permissions.',
),
).toBeDefined();
expect(getByText('Remove BIP-44 Test Snap')).toBeDefined();
expect(getByRole('button')).toHaveClass(
'button btn--rounded btn-danger view-snap__remove-button',
);
});
});

View File

@ -280,7 +280,7 @@ class SettingsPage extends PureComponent {
{
content: t('snaps'),
icon: (
<i className="fa fa-flask" title={t('snapsSettingsDescription')} />
<Icon name={ICON_NAMES.SNAPS} title={t('snapsSettingsDescription')} />
),
key: SNAPS_LIST_ROUTE,
},

View File

@ -55,6 +55,10 @@ import {
shortenAddress,
getAccountByAddress,
getURLHostName,
///: BEGIN:ONLY_INCLUDE_IN(flask)
removeSnapIdPrefix,
getSnapName,
///: END:ONLY_INCLUDE_IN
} from '../helpers/utils/util';
import { TEMPLATED_CONFIRMATION_MESSAGE_TYPES } from '../pages/confirmation/templates';
@ -1459,3 +1463,25 @@ export function getIsDesktopEnabled(state) {
return state.metamask.desktopEnabled;
}
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(flask)
/**
* To get all installed snaps with proper metadata
*
* @param {*} state
* @returns Boolean
*/
export function getSnapsList(state) {
const snaps = getSnaps(state);
return Object.entries(snaps).map(([key, snap]) => {
const targetSubjectMetadata = getTargetSubjectMetadata(state, snap?.id);
return {
key,
id: snap.id,
packageName: removeSnapIdPrefix(snap.id),
name: getSnapName(snap.id, targetSubjectMetadata),
};
});
}
///: END:ONLY_INCLUDE_IN