diff --git a/ui/StatusQ/doc/src/images/status_warning_box.png b/ui/StatusQ/doc/src/images/status_warning_box.png new file mode 100644 index 0000000000..98e6bc2b81 Binary files /dev/null and b/ui/StatusQ/doc/src/images/status_warning_box.png differ diff --git a/ui/StatusQ/src/StatusQ/Controls/StatusWarningBox.qml b/ui/StatusQ/src/StatusQ/Controls/StatusWarningBox.qml new file mode 100644 index 0000000000..7ccd470779 --- /dev/null +++ b/ui/StatusQ/src/StatusQ/Controls/StatusWarningBox.qml @@ -0,0 +1,111 @@ +import QtQuick 2.15 +import QtQuick.Layouts 1.15 +import QtQuick.Controls 2.15 +import StatusQ.Core 0.1 +import StatusQ.Core.Theme 0.1 + +/*! + \qmltype StatusWarningBox + \inherits Control + \inqmlmodule StatusQ.Controls + \since StatusQ.Controls 0.1 + \brief Displays a customizable WarningBox component. + Inherits \l{https://doc.qt.io/qt-5/qml-qtquick-controls2-control.html}{Item}. + + The \c StatusWarningBox displays an customizable WarningBox for users to show an icon and text. + For example: + + \qml + StatusWarningBox { + icon: "caution" + text: qsTr("Warning!") + bgColor: Theme.palette.warningColor1 + } + \endqml + + \image status_warning_box.png + + For a list of components available see StatusQ. +*/ + +Control { + id: root + implicitWidth: 614 + implicitHeight: rowContent.height + + /*! + \qmlproperty alias StatusWarningBox::text + This property holds a reference to the StatusBaseText component's text property and displays the + warning text. + */ + property alias text: warningText.text + /*! + \qmlproperty string StatusWarningBox::icon + This property sets the StatusWarningBox icon. + */ + property string icon + /*! + \qmlproperty string StatusWarningBox::iconColor + This property sets the StatusWarningBox icon color. + */ + property string iconColor: "transparent" + /*! + \qmlproperty string StatusWarningBox::bgColor + This property sets the StatusWarningBox background color. + */ + property string bgColor: "transparent" + /*! + \qmlproperty string StatusWarningBox::borderColor + This property sets the StatusWarningBox border color. + */ + property string borderColor: Theme.palette.warningColor1 + /*! + \qmlproperty string StatusWarningBox::textColor + This property sets the StatusWarningBox text color. + */ + property string textColor: Theme.palette.warningColor1 + + background: Rectangle { + radius: 8 + opacity: 0.5 + border.color: root.borderColor + color: "transparent" + Rectangle { + anchors.fill: parent + color: root.bgColor + radius: 8 + opacity: 0.2 + } + } + + contentItem: Item { + id: rowContent + width: parent.width + height: (row.height+32)//xlPadding + RowLayout { + id: row + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: 16 + anchors.rightMargin: 16 + anchors.verticalCenter: parent.verticalCenter + height: warningText.contentHeight + spacing: 6 + StatusIcon { + Layout.alignment: Qt.AlignTop + icon: root.icon + color: root.iconColor + } + StatusBaseText { + id: warningText + Layout.fillWidth: true + Layout.preferredHeight: contentHeight + wrapMode: Text.WordWrap + verticalAlignment: Text.AlignVCenter + font.pixelSize: Theme.primaryTextFontSize + color: root.textColor + } + } + } +} + diff --git a/ui/StatusQ/src/StatusQ/Controls/qmldir b/ui/StatusQ/src/StatusQ/Controls/qmldir index 7e7edf8dab..c76064faf8 100644 --- a/ui/StatusQ/src/StatusQ/Controls/qmldir +++ b/ui/StatusQ/src/StatusQ/Controls/qmldir @@ -57,3 +57,4 @@ StatusLinkText 0.1 StatusLinkText.qml StatusImageSelector 0.1 StatusImageSelector.qml StatusColorRadioButton 0.1 StatusColorRadioButton.qml StatusBlockProgressBar 0.1 StatusBlockProgressBar.qml +StatusWarningBox 0.1 StatusWarningBox.qml diff --git a/ui/StatusQ/src/assets.qrc b/ui/StatusQ/src/assets.qrc index ef9d29bc2b..ef7b6f080d 100644 --- a/ui/StatusQ/src/assets.qrc +++ b/ui/StatusQ/src/assets.qrc @@ -10405,5 +10405,6 @@ assets/twemoji/svg/e50a.svg assets/twemoji/LICENSE assets/twemoji/twemoji.js + assets/img/icons/caution.svg diff --git a/ui/StatusQ/src/assets/img/icons/caution.svg b/ui/StatusQ/src/assets/img/icons/caution.svg new file mode 100644 index 0000000000..65d87c4aa6 --- /dev/null +++ b/ui/StatusQ/src/assets/img/icons/caution.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/StatusQ/src/statusq.qrc b/ui/StatusQ/src/statusq.qrc index 4bb10358cd..53e93f4a4e 100644 --- a/ui/StatusQ/src/statusq.qrc +++ b/ui/StatusQ/src/statusq.qrc @@ -224,5 +224,6 @@ StatusQ/Core/Utils/big.min.mjs StatusQ/Controls/StatusBlockProgressBar.qml StatusQ/Components/StatusInfoBoxPanel.qml + StatusQ/Controls/StatusWarningBox.qml diff --git a/ui/app/AppLayouts/Communities/stores/CommunitiesStore.qml b/ui/app/AppLayouts/Communities/stores/CommunitiesStore.qml index 07fff2d640..ea103642ab 100644 --- a/ui/app/AppLayouts/Communities/stores/CommunitiesStore.qml +++ b/ui/app/AppLayouts/Communities/stores/CommunitiesStore.qml @@ -57,7 +57,7 @@ QtObject { property string communityTags: communitiesModuleInst.tags signal importingCommunityStateChanged(string communityId, int state, string errorMsg) - + signal communityPrivateKeyRemoved(string communityId) signal communityInfoAlreadyRequested() @@ -96,6 +96,10 @@ QtObject { root.communitiesModuleInst.importCommunity(communityKey); } + function getCommunityPublicKeyFromPrivateKey(privateKey) { + return root.communitiesModuleInst.getCommunityPublicKeyFromPrivateKey(privateKey); + } + function requestCommunityInfo(communityKey, importing = false) { const publicKey = Utils.isCompressedPubKey(communityKey) ? Utils.changeCommunityKeyCompression(communityKey) diff --git a/ui/app/AppLayouts/stores/RootStore.qml b/ui/app/AppLayouts/stores/RootStore.qml index 7c8232a775..7b3ded4820 100644 --- a/ui/app/AppLayouts/stores/RootStore.qml +++ b/ui/app/AppLayouts/stores/RootStore.qml @@ -4,6 +4,7 @@ import utils 1.0 import SortFilterProxyModel 0.2 import AppLayouts.Wallet.stores 1.0 as WalletStore + import "../Profile/stores" QtObject { diff --git a/ui/app/mainui/Popups.qml b/ui/app/mainui/Popups.qml index ed9823cdf1..d3a8949b06 100644 --- a/ui/app/mainui/Popups.qml +++ b/ui/app/mainui/Popups.qml @@ -18,6 +18,7 @@ import shared.popups 1.0 import shared.status 1.0 import utils 1.0 +import AppLayouts.Wallet.stores 1.0 as WalletStore QtObject { id: root @@ -518,7 +519,7 @@ QtObject { id: communityIntroDialog property string communityId loginType: root.rootStore.loginType - walletAccountsModel: root.rootStore.receiveAccounts + walletAccountsModel: root.rootStore.walletAccountsModel permissionsModel: root.rootStore.permissionsModel assetsModel: root.rootStore.assetsModel collectiblesModel: root.rootStore.collectiblesModel diff --git a/ui/imports/shared/popups/ImportCommunityPopup.qml b/ui/imports/shared/popups/ImportCommunityPopup.qml index df3e1c69c6..d5a1c6aa55 100644 --- a/ui/imports/shared/popups/ImportCommunityPopup.qml +++ b/ui/imports/shared/popups/ImportCommunityPopup.qml @@ -23,13 +23,24 @@ StatusDialog { QtObject { id: d property string importErrorMessage - readonly property bool communityFound: (d.isPublicKey && !!d.communityDetails) - readonly property var communityDetails: root.store.getCommunityDetails(publicKey) + + readonly property bool communityFound: (d.communityDetails !== null && !!d.communityDetails.name) + readonly property var communityDetails: { + if (isInputValid) { + let key = isPublicKey ? Utils.getCompressedPk(publicKey) : + root.store.getCommunityPublicKeyFromPrivateKey(inputKey); + return root.store.getCommunityDetails(key); + } else { + return null; + } + } + readonly property string inputErrorMessage: isInputValid ? "" : qsTr("Invalid key") readonly property string errorMessage: importErrorMessage || inputErrorMessage readonly property string inputKey: keyInput.text.trim() readonly property bool isPrivateKey: (Utils.isPrivateKey(inputKey)) readonly property bool isPublicKey: (publicKey !== "") + readonly property string privateKey: inputKey readonly property string publicKey: { if (!Utils.isStatusDeepLink(inputKey)) { const key = Utils.dropCommunityLinkPrefix(inputKey) @@ -49,19 +60,15 @@ StatusDialog { rightButtons: ObjectModel { StatusButton { id: importButton - enabled: d.isInputValid - loading: (d.isPublicKey && !d.communityFound) - text: d.isPrivateKey ? qsTr("Make this an Owner Node") - : qsTr("Import") + enabled: (d.isInputValid && (d.isPrivateKey && d.communityFound ? agreeToKeepOnline.checked : true)) + loading: (enabled && !d.communityFound) + text: !d.publicKey ? qsTr("Make this device the control node for %1").arg((!loading && !!d.communityDetails) ? d.communityDetails.name : "") + : qsTr("Import") onClicked: { if (d.isPrivateKey) { - const communityKey = d.inputKey - if (!communityKey.startsWith("0x")) { - communityKey = "0x" + communityKey; - } - root.store.importCommunity(communityKey); + root.store.importCommunity(d.privateKey); root.close(); - } else if (d.communityFound) { + } else if (d.isPublicKey) { root.joinCommunity(d.publicKey, d.communityDetails); } } @@ -69,65 +76,99 @@ StatusDialog { } } - ColumnLayout { + + StatusScrollView { + id: scrollContent anchors.fill: parent - spacing: Style.current.padding + anchors.leftMargin: Style.current.halfPadding + contentWidth: (root.width-Style.current.bigPadding-Style.current.padding) + padding: 0 - StatusBaseText { - id: infoText1 - Layout.fillWidth: true - text: qsTr("Enter the public key of the community you wish to access, or enter the private key of a community you own. Remember to always keep any private key safe and never share a private key with anyone else.") - wrapMode: Text.WordWrap - font.pixelSize: 13 - color: Theme.palette.baseColor1 - } + ColumnLayout { + width: (scrollContent.width-Style.current.padding) + spacing: Style.current.halfPadding - StatusBaseText { - id: inputLabel - text: qsTr("Community key") - color: Theme.palette.directColor1 - font.pixelSize: 15 - } - - StatusTextArea { - id: keyInput - Layout.fillWidth: true - implicitHeight: 110 - placeholderText: "0x0..." - wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere - onTextChanged: d.importErrorMessage = "" - } - RowLayout { - Layout.fillWidth: true - Layout.fillHeight: true - StatusChatInfoButton { - visible: (d.communityFound && d.isPublicKey) - title: !!d.communityDetails.name ? d.communityDetails.name : "" - subTitle: !!d.communityDetails.nbMembers ? qsTr("%n member(s)", "", d.communityDetails.nbMembers) : "" - asset.emoji: "1f918" - asset.emojiSize: "24x24" - asset.name: !!d.communityDetails.image ? d.communityDetails.image : "" - asset.isImage: (asset.name !== "") - asset.color: !!d.communityDetails.color ? d.communityDetails.color : "" - } - Item { Layout.fillWidth: true } StatusBaseText { - id: detectionLabel - Layout.alignment: Qt.AlignRight + id: infoText1 + Layout.fillWidth: true + text: qsTr("Enter the public key of the community you wish to access, or enter the private key of a community you own. Remember to always keep any private key safe and never share a private key with anyone else.") + wrapMode: Text.WordWrap font.pixelSize: 13 - visible: keyInput.text.trim() !== "" - text: { - if (d.errorMessage !== "") { - return d.errorMessage - } - if (d.isPrivateKey) { - return qsTr("Private key detected") - } - if (d.isPublicKey) { - return qsTr("Public key detected") - } + color: Theme.palette.baseColor1 + } + + StatusBaseText { + id: inputLabel + text: qsTr("Community key") + color: Theme.palette.directColor1 + font.pixelSize: 15 + } + + StatusTextArea { + id: keyInput + Layout.fillWidth: true + implicitHeight: 108 + placeholderText: "0x0..." + wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere + onTextChanged: d.importErrorMessage = "" + } + RowLayout { + Layout.fillWidth: true + Layout.minimumHeight: 46 + Layout.maximumHeight: 46 + StatusChatInfoButton { + visible: d.communityFound + title: visible ? d.communityDetails.name : "" + subTitle: visible ? qsTr("%n member(s)", "", d.communityDetails.nbMembers) : "" + asset.name: visible ? d.communityDetails.image : "" + asset.isImage: (asset.name !== "") + asset.color: visible ? d.communityDetails.color : "" + } + Item { Layout.fillWidth: true } + StatusBaseText { + id: detectionLabel + Layout.alignment: Qt.AlignRight + font.pixelSize: 13 + visible: keyInput.text.trim() !== "" + text: { + if (d.errorMessage !== "") { + return d.errorMessage + } + if (d.isPrivateKey) { + return qsTr("Private key detected") + } + if (d.isPublicKey) { + return qsTr("Public key detected") + } + } + color: d.errorMessage === "" ? Theme.palette.successColor1 : Theme.palette.dangerColor1 + } + } + ColumnLayout { + Layout.fillWidth: true + Layout.fillHeight: true + visible: (d.communityFound && d.isPrivateKey) + Layout.topMargin: 12 + spacing: Style.current.padding + StatusWarningBox { + Layout.fillWidth: true + icon: "caution" + text: qsTr("Another device might currently have the control node for this Community. Running multiple control nodes will cause unforeseen issues. Make sure you delete the private key in that other device in the community management tab.") + bgColor: borderColor + } + StatusDialogDivider { Layout.fillWidth: true; Layout.topMargin: Style.current.padding } + StatusBaseText { + Layout.topMargin: Style.current.halfPadding + visible: (d.communityFound && d.isPrivateKey) + font.pixelSize: Style.current.primaryTextFontSize + text: qsTr("I acknowledge that...") + } + StatusCheckBox { + id: agreeToKeepOnline + Layout.fillWidth: true + font.pixelSize: Style.current.primaryTextFontSize + text: qsTr("I must keep this device online and running Status for the Community to function") } - color: d.errorMessage === "" ? Theme.palette.successColor1 : Theme.palette.dangerColor1 } } }