feat(desktop/wallet2) Add account with seed modal
Added adding account with seed phrase feature Closes #3311
This commit is contained in:
parent
470144db6a
commit
24b704f398
|
@ -0,0 +1,47 @@
|
||||||
|
import QtQuick 2.13
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
|
||||||
|
import StatusQ.Controls 0.1
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: root
|
||||||
|
spacing: 8
|
||||||
|
signal generateAccountClicked()
|
||||||
|
signal proceedWithSeedClicked()
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
font.pixelSize: 15
|
||||||
|
text: qsTr("Is your seed phrase secure?")
|
||||||
|
color: Theme.palette.dangerColor1
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
Layout.preferredWidth: 345
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
font.pixelSize: 15
|
||||||
|
text: qsTr("We found no active accounts with that seed phrase. If it is a new account please ensure that it is secure. Scammers often provide you with a phrase and siphon funds later.\n")
|
||||||
|
color: Theme.palette.baseColor1
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusButton {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
text: qsTr("Generate an account using Status")
|
||||||
|
onClicked: {
|
||||||
|
root.generateAccountClicked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusButton {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
type: StatusBaseButton.Type.Danger
|
||||||
|
text: qsTr("Proceed with seed phrase")
|
||||||
|
onClicked: {
|
||||||
|
root.proceedWithSeedClicked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,8 +14,7 @@ import StatusQ.Popups 0.1
|
||||||
|
|
||||||
StatusModal {
|
StatusModal {
|
||||||
id: popup
|
id: popup
|
||||||
width: 574
|
height: (keyOrSeedPhraseInput.input.edit.contentHeight > 56 || seedPhraseInserted) ? 517 : 498
|
||||||
height: (keyOrSeedPhraseInput.height > 100) ? 517 : 498
|
|
||||||
header.title: qsTr("Add account")
|
header.title: qsTr("Add account")
|
||||||
onOpened: {
|
onOpened: {
|
||||||
keyOrSeedPhraseInput.input.edit.forceActiveFocus(Qt.MouseFocusReason);
|
keyOrSeedPhraseInput.input.edit.forceActiveFocus(Qt.MouseFocusReason);
|
||||||
|
@ -34,7 +33,17 @@ StatusModal {
|
||||||
}
|
}
|
||||||
|
|
||||||
function validate() {
|
function validate() {
|
||||||
return (keyOrSeedPhraseInput.valid && accountNameInput.valid);
|
if (popup.isSeedCountValid && !popup.seedPhraseNotFound()) {
|
||||||
|
var validCount = 0;
|
||||||
|
var accountsList = seedAccountDetails.activeAccountsList;
|
||||||
|
for (var i = 0; i < accountsList.count; i++) {
|
||||||
|
if (accountsList.itemAtIndex(i).nameInputValid) {
|
||||||
|
validCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (popup.isSeedCountValid && !popup.seedPhraseNotFound()) ? (validCount === accountsList.count) :
|
||||||
|
(keyOrSeedPhraseInput.valid && pkeyAccountDetails.nameInputValid);
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: Item {
|
contentItem: Item {
|
||||||
|
@ -46,10 +55,7 @@ StatusModal {
|
||||||
height: parent.height
|
height: parent.height
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: seedOrPKInputContainer
|
id: leftContent
|
||||||
width: parent.width
|
|
||||||
height: 120 + ((keyOrSeedPhraseInput.height > 100) ? 30 : 0)
|
|
||||||
|
|
||||||
StatusInput {
|
StatusInput {
|
||||||
id: keyOrSeedPhraseInput
|
id: keyOrSeedPhraseInput
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
@ -74,6 +80,12 @@ StatusModal {
|
||||||
]
|
]
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
popup.seedPhraseInserted = keyOrSeedPhraseInput.text.includes(" ");
|
popup.seedPhraseInserted = keyOrSeedPhraseInput.text.includes(" ");
|
||||||
|
if (popup.seedPhraseInserted) {
|
||||||
|
popup.seedPhraseInserted = true;
|
||||||
|
seedAccountDetails.searching = true;
|
||||||
|
seedAccountDetails.timer.start();
|
||||||
|
}
|
||||||
|
|
||||||
popup.isSeedCountValid = (!!keyOrSeedPhraseInput.text && (keyOrSeedPhraseInput.text.match(/(\w+)/g).length === 12));
|
popup.isSeedCountValid = (!!keyOrSeedPhraseInput.text && (keyOrSeedPhraseInput.text.match(/(\w+)/g).length === 12));
|
||||||
if (text === "") {
|
if (text === "") {
|
||||||
errorMessage = qsTr("You need to enter a valid private key or seed phrase");
|
errorMessage = qsTr("You need to enter a valid private key or seed phrase");
|
||||||
|
@ -95,68 +107,93 @@ StatusModal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Separator {
|
Rectangle {
|
||||||
id: separator
|
id: separator
|
||||||
anchors.left: parent.left
|
color: Theme.palette.statusPopupMenu.separatorColor
|
||||||
anchors.leftMargin: 16
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.rightMargin: 16
|
|
||||||
anchors.top: seedOrPKInputContainer.bottom
|
|
||||||
anchors.topMargin: (2*popup.marginBetweenInputs)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
PKeyAccountDetails {
|
||||||
id: accountNameInputRow
|
id: pkeyAccountDetails
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.rightMargin: 10
|
|
||||||
anchors.top: separator.bottom
|
|
||||||
anchors.topMargin: popup.marginBetweenInputs
|
|
||||||
height: (parent.height/2)
|
|
||||||
StatusInput {
|
|
||||||
id: accountNameInput
|
|
||||||
implicitWidth: (parent.width - emojiDropDown.width)
|
|
||||||
input.implicitHeight: 56
|
|
||||||
input.placeholderText: qsTrId("enter-an-account-name...")
|
|
||||||
label: qsTrId("account-name")
|
|
||||||
validators: [StatusMinLengthValidator { minLength: 1 }]
|
|
||||||
onTextChanged: {
|
|
||||||
errorMessage = (accountNameInput.text === "") ?
|
|
||||||
qsTrId("you-need-to-enter-an-account-name") : ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Item {
|
|
||||||
id: emojiDropDown
|
|
||||||
//emoji placeholder
|
|
||||||
width: 80
|
|
||||||
height: parent.height
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.topMargin: 11
|
|
||||||
StyledText {
|
|
||||||
id: inputLabel
|
|
||||||
text: "Emoji"
|
|
||||||
font.weight: Font.Medium
|
|
||||||
font.pixelSize: 13
|
|
||||||
color: Style.current.textColor
|
|
||||||
}
|
|
||||||
Rectangle {
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 56
|
height: parent.height/2
|
||||||
anchors.top: inputLabel.bottom
|
anchors.top: separator.bottom
|
||||||
anchors.topMargin: 7
|
}
|
||||||
radius: 10
|
|
||||||
color: "pink"
|
SeedAddAccountView {
|
||||||
opacity: 0.6
|
id: seedAccountDetails
|
||||||
}
|
width: (parent.width/2)
|
||||||
|
height: parent.height
|
||||||
|
anchors.right: parent.right
|
||||||
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
when: (popup.isSeedCountValid && !popup.seedPhraseNotFound())
|
||||||
|
PropertyChanges {
|
||||||
|
target: popup
|
||||||
|
width: 907
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: pkeyAccountDetails
|
||||||
|
opacity: 0.0
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: leftContent
|
||||||
|
width: contentItem.width/2
|
||||||
|
height: contentItem.height
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: separator
|
||||||
|
width: 1
|
||||||
|
height: contentItem.height
|
||||||
|
}
|
||||||
|
AnchorChanges {
|
||||||
|
target: separator
|
||||||
|
anchors.left: leftContent.right
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: seedAccountDetails
|
||||||
|
opacity: 1.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
when: !(popup.isSeedCountValid && !popup.seedPhraseNotFound())
|
||||||
|
PropertyChanges {
|
||||||
|
target: popup
|
||||||
|
width: 574
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: seedAccountDetails
|
||||||
|
opacity: 0.0
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: leftContent
|
||||||
|
width: contentItem.width
|
||||||
|
height: 120
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: pkeyAccountDetails
|
||||||
|
opacity: 1.0
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: separator
|
||||||
|
width: contentItem.width
|
||||||
|
height: 1
|
||||||
|
anchors.topMargin: (2*popup.marginBetweenInputs)
|
||||||
|
}
|
||||||
|
AnchorChanges {
|
||||||
|
target: separator
|
||||||
|
anchors.left: contentItem.left
|
||||||
|
anchors.top: leftContent.bottom
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
rightButtons: [
|
rightButtons: [
|
||||||
StatusButton {
|
StatusButton {
|
||||||
text: popup.loading ? qsTrId("loading") : qsTrId("add-account")
|
text: popup.loading ? qsTrId("loading") : qsTrId("add-account")
|
||||||
enabled: !popup.loading && (accountNameInput.text !== "")
|
enabled: (!popup.loading && popup.validate())
|
||||||
&& (keyOrSeedPhraseInput.correctWordCount || (keyOrSeedPhraseInput.text !== ""))
|
|
||||||
|
|
||||||
MessageDialog {
|
MessageDialog {
|
||||||
id: accountError
|
id: accountError
|
||||||
|
@ -172,14 +209,25 @@ StatusModal {
|
||||||
popup.loading = false;
|
popup.loading = false;
|
||||||
} else {
|
} else {
|
||||||
//TODO account color to be verified with design
|
//TODO account color to be verified with design
|
||||||
const result = popup.seedPhraseInserted ?
|
var result;
|
||||||
walletModel.accountsView.addAccountsFromSeed(keyOrSeedPhraseInput.text, "", accountNameInput.text, "") :
|
if (popup.isSeedCountValid && !popup.seedPhraseNotFound()) {
|
||||||
walletModel.accountsView.addAccountsFromPrivateKey(keyOrSeedPhraseInput.text, "", accountNameInput.text, "");
|
var accountsList = seedAccountDetails.activeAccountsList;
|
||||||
|
for (var i = 0; i < accountsList.count; i++) {
|
||||||
|
//TODO remove password requirement
|
||||||
|
if (!!accountsList.itemAtIndex(i)) {
|
||||||
|
result = walletModel.accountsView.addAccountsFromSeed(accountsList.itemAtIndex(i).accountAddress, "", accountsList.itemAtIndex(i).accountName, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = walletModel.accountsView.addAccountsFromPrivateKey(keyOrSeedPhraseInput.text, "", pkeyAccountDetails.accountName, "");
|
||||||
|
}
|
||||||
popup.loading = false;
|
popup.loading = false;
|
||||||
if (result) {
|
if (result) {
|
||||||
let resultJson = JSON.parse(result);
|
let resultJson = JSON.parse(result);
|
||||||
|
if (!Utils.isInvalidPasswordMessage(resultJson.error)) {
|
||||||
accountError.text = resultJson.error;
|
accountError.text = resultJson.error;
|
||||||
accountError.open();
|
accountError.open();
|
||||||
|
}
|
||||||
errorSound.play();
|
errorSound.play();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
import QtQuick 2.13
|
||||||
|
|
||||||
|
import "../../../../imports"
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
import StatusQ.Components 0.1
|
||||||
|
import StatusQ.Controls 0.1
|
||||||
|
import StatusQ.Controls.Validators 0.1
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
visible: (opacity > 0.1)
|
||||||
|
|
||||||
|
property string emoji: "" //TBD
|
||||||
|
property string accountName: accountNameInput.text
|
||||||
|
property bool nameInputValid: accountNameInput.valid
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: 10
|
||||||
|
StatusInput {
|
||||||
|
id: accountNameInput
|
||||||
|
width: (parent.width - 100)
|
||||||
|
input.implicitHeight: 56
|
||||||
|
input.placeholderText: qsTrId("enter-an-account-name...")
|
||||||
|
label: qsTrId("account-name")
|
||||||
|
validators: [StatusMinLengthValidator { minLength: 1 }]
|
||||||
|
onTextChanged: {
|
||||||
|
errorMessage = (accountNameInput.text === "") ?
|
||||||
|
qsTrId("you-need-to-enter-an-account-name") : ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
//emoji placeholder
|
||||||
|
width: 80
|
||||||
|
height: parent.height
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 11
|
||||||
|
StatusBaseText {
|
||||||
|
id: inputLabel
|
||||||
|
text: "Emoji"
|
||||||
|
font.weight: Font.Medium
|
||||||
|
font.pixelSize: 13
|
||||||
|
color: Style.current.textColor
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 56
|
||||||
|
anchors.top: inputLabel.bottom
|
||||||
|
anchors.topMargin: 7
|
||||||
|
radius: 10
|
||||||
|
color: "pink"
|
||||||
|
opacity: 0.6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
import QtQuick 2.13
|
||||||
|
import QtQuick.Controls 2.12
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
|
||||||
|
import "../../../../imports"
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
import StatusQ.Components 0.1
|
||||||
|
import StatusQ.Controls 0.1
|
||||||
|
import StatusQ.Controls.Validators 0.1
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
width: parent.width
|
||||||
|
height: 120
|
||||||
|
|
||||||
|
property bool deleteButtonVisible
|
||||||
|
property string accountName: accountNameInput.text
|
||||||
|
property bool nameInputValid: accountNameInput.valid
|
||||||
|
property string accountAddress: model.address
|
||||||
|
|
||||||
|
property string emoji: "" //TODO implement emoji selection
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: 0
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
StatusInput {
|
||||||
|
id: accountNameInput
|
||||||
|
anchors.fill: parent
|
||||||
|
input.implicitHeight: 56
|
||||||
|
input.placeholderText: qsTrId("enter-an-account-name...")
|
||||||
|
label: "Ledger" //TODO replace with derivation path, for now use Ledger
|
||||||
|
secondaryLabel: address.replace(address.substring(6, (address.length-4)), "...")
|
||||||
|
validators: [StatusMinLengthValidator { minLength: 1 }]
|
||||||
|
onTextChanged: {
|
||||||
|
errorMessage = (accountNameInput.text === "") ?
|
||||||
|
qsTrId("you-need-to-enter-an-account-name") : ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
//emoji placeholder
|
||||||
|
Layout.preferredWidth: 80
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
Layout.topMargin: 8
|
||||||
|
StatusBaseText {
|
||||||
|
id: inputLabel
|
||||||
|
text: "Emoji"
|
||||||
|
font.weight: Font.Medium
|
||||||
|
font.pixelSize: 13
|
||||||
|
color: Style.current.textColor
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 56
|
||||||
|
anchors.top: inputLabel.bottom
|
||||||
|
anchors.topMargin: 7
|
||||||
|
radius: 10
|
||||||
|
color: "pink"
|
||||||
|
opacity: 0.6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Control {
|
||||||
|
id: deleteButton
|
||||||
|
Layout.preferredWidth: 50
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.alignment: Qt.AlignBottom
|
||||||
|
visible: root.deleteButtonVisible
|
||||||
|
background: Item {
|
||||||
|
anchors.fill: deleteButton
|
||||||
|
StatusIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
color: Theme.palette.baseColor1
|
||||||
|
icon: "delete"
|
||||||
|
}
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
walletModel.accountsView.deleteAccount(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
import QtQuick 2.13
|
||||||
|
|
||||||
|
import "../../../../imports"
|
||||||
|
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
import StatusQ.Components 0.1
|
||||||
|
import StatusQ.Controls 0.1
|
||||||
|
import StatusQ.Controls.Validators 0.1
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
visible: (opacity > 0.1)
|
||||||
|
property bool searching: false
|
||||||
|
property alias activeAccountsList: activeAccountsView
|
||||||
|
property Timer timer: Timer {
|
||||||
|
interval: 800
|
||||||
|
onTriggered: {
|
||||||
|
searching = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
property var dummyModel: []
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: searchingColumn
|
||||||
|
width: parent.width
|
||||||
|
height: 80
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: 15
|
||||||
|
StatusLoadingIndicator {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
color: Theme.palette.primaryColor1
|
||||||
|
}
|
||||||
|
StatusBaseText {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
font.pixelSize: 15
|
||||||
|
text: qsTr("Searching for active accounts")
|
||||||
|
color: Theme.palette.baseColor1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: activeAccountsView
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.bottomMargin: 10
|
||||||
|
clip: true
|
||||||
|
//TODO replace with active accounts model
|
||||||
|
model: walletModel.accountsView.accounts
|
||||||
|
delegate: SeedAccountDetailsDelegate {
|
||||||
|
deleteButtonVisible: (activeAccountsView.count > 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountNotFound {
|
||||||
|
id: accountNotFound
|
||||||
|
width: parent.width
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
when: searching
|
||||||
|
PropertyChanges {
|
||||||
|
target: searchingColumn
|
||||||
|
opacity: 1.0
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: activeAccountsView
|
||||||
|
opacity: 0.0
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: accountNotFound
|
||||||
|
opacity: 0.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
when: !searching
|
||||||
|
PropertyChanges {
|
||||||
|
target: searchingColumn
|
||||||
|
opacity: 0.0
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: activeAccountsView
|
||||||
|
opacity: 1.0
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: accountNotFound
|
||||||
|
opacity: 0.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
when: (activeAccountsView.count === 0 && !searching)
|
||||||
|
PropertyChanges {
|
||||||
|
target: searchingColumn
|
||||||
|
opacity: 0.0
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: activeAccountsView
|
||||||
|
opacity: 0.0
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: accountNotFound
|
||||||
|
opacity: 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue