parent
f29e825af4
commit
0903fa6ce2
|
@ -1 +1 @@
|
|||
Subproject commit 41f67b59e954550207fb8882a5b58dd705cc4a38
|
||||
Subproject commit b9aa49853adfb0015b7b9572d0a72ad6ebe54e7c
|
|
@ -381,6 +381,7 @@ Item {
|
|||
id: cmpSendTransactionWithEns
|
||||
SendModal {
|
||||
id: sendTransactionWithEns
|
||||
anchors.centerIn: parent
|
||||
store: root.rootStore
|
||||
contactsStore: root.contactsStore
|
||||
onOpened: {
|
||||
|
@ -390,9 +391,8 @@ Item {
|
|||
onClosed: {
|
||||
destroy()
|
||||
}
|
||||
isContact: root.isContact
|
||||
selectRecipient.readOnly: true
|
||||
selectRecipient.selectedRecipient: {
|
||||
launchedFromChat: true
|
||||
preSelectedRecipient: {
|
||||
parentModule.prepareChatContentModuleForChatId(activeChatId)
|
||||
let chatContentModule = parentModule.getChatContentModule()
|
||||
|
||||
|
@ -405,7 +405,6 @@ Item {
|
|||
ensVerified: true
|
||||
}
|
||||
}
|
||||
selectRecipient.selectedType: RecipientSelector.Type.Contact
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import QtQuick.Layouts 1.13
|
|||
|
||||
import StatusQ.Popups 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
||||
import utils 1.0
|
||||
|
||||
|
|
|
@ -53,6 +53,14 @@ QtObject {
|
|||
|
||||
property var walletSectionTransactionsInst: walletSectionTransactions
|
||||
|
||||
property bool isMultiNetworkEnabled: localAccountSensitiveSettings.isMultiNetworkEnabled
|
||||
|
||||
property var savedAddressesModel: walletSectionSavedAddresses.model
|
||||
|
||||
function getEtherscanLink() {
|
||||
return profileSectionModule.ensUsernamesModule.getEtherscanLink()
|
||||
}
|
||||
|
||||
function createCommunity(communityName, communityDescription, checkedMembership, ensOnlySwitchChecked, communityColor, communityImage, imageCropperModalaX, imageCropperModalaY, imageCropperModalbX, imageCropperModalbY) {
|
||||
communitiesModuleInst.createCommunity(communityName, communityDescription, checkedMembership, ensOnlySwitchChecked, communityColor, communityImage, imageCropperModalaX, imageCropperModalaY, imageCropperModalbX, imageCropperModalbY);
|
||||
}
|
||||
|
@ -104,4 +112,8 @@ QtObject {
|
|||
function suggestedFees() {
|
||||
return JSON.parse(walletSectionTransactions.suggestedFees())
|
||||
}
|
||||
|
||||
function hex2Eth(value) {
|
||||
return globalUtils.hex2Eth(value)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -801,6 +801,7 @@ Item {
|
|||
}
|
||||
property var selectedAccount
|
||||
sourceComponent: SendModal {
|
||||
anchors.centerIn: parent
|
||||
store: appMain.rootStore
|
||||
contactsStore: appMain.rootStore.profileSectionStore.contactsStore
|
||||
onOpened: {
|
||||
|
@ -813,7 +814,7 @@ Item {
|
|||
}
|
||||
onLoaded: {
|
||||
if(!!sendModal.selectedAccount) {
|
||||
item.selectFromAccount.selectedAccount = sendModal.selectedAccount
|
||||
item.preSelectedAccount = sendModal.selectedAccount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
import QtQuick 2.14
|
||||
import QtQuick.Controls 2.14
|
||||
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
||||
StatusInput {
|
||||
id: cursorInput
|
||||
|
||||
property string cursorColor: Theme.palette.primaryColor1
|
||||
|
||||
height: input.edit.height
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
|
||||
input.placeholderText: ""
|
||||
input.edit.cursorVisible: true
|
||||
input.edit.font.pixelSize: 32
|
||||
input.placeholderFont.pixelSize: 32
|
||||
input.leftPadding: 0
|
||||
input.rightPadding: 0
|
||||
input.topPadding: 0
|
||||
input.bottomPadding: 0
|
||||
input.edit.padding: 0
|
||||
input.background.color: "transparent"
|
||||
input.background.border.width: 0
|
||||
// To-do this needs to be removed once https://github.com/status-im/StatusQ/issues/578 is implemented and cursor is moved to StatusInput
|
||||
input.edit.cursorDelegate: Rectangle {
|
||||
id: cursor
|
||||
visible: input.edit.cursorVisible
|
||||
color: cursorColor
|
||||
width: 2
|
||||
|
||||
SequentialAnimation {
|
||||
loops: Animation.Infinite
|
||||
running: input.edit.cursorVisible
|
||||
|
||||
PropertyAction {
|
||||
target: cursor
|
||||
property: 'visible'
|
||||
value: true
|
||||
}
|
||||
|
||||
PauseAnimation {
|
||||
duration: 600
|
||||
}
|
||||
|
||||
PropertyAction {
|
||||
target: cursor
|
||||
property: 'visible'
|
||||
value: false
|
||||
}
|
||||
|
||||
PauseAnimation {
|
||||
duration: 600
|
||||
}
|
||||
|
||||
onStopped: {
|
||||
cursor.visible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -62,6 +62,8 @@ Item {
|
|||
|
||||
Input {
|
||||
id: chatKey
|
||||
property bool hasValidSearchResult: false
|
||||
|
||||
//% "Enter ENS username or chat key"
|
||||
placeholderText: qsTrId("enter-contact-code")
|
||||
visible: showSearch
|
||||
|
@ -90,6 +92,7 @@ Item {
|
|||
return;
|
||||
}
|
||||
|
||||
chatKey.hasValidSearchResult = false
|
||||
Qt.callLater(resolveENS, chatKey.text);
|
||||
} else {
|
||||
root.validationError = "";
|
||||
|
@ -100,6 +103,7 @@ Item {
|
|||
Connections {
|
||||
target: mainModule
|
||||
onResolvedENS: {
|
||||
chatKey.hasValidSearchResult = false
|
||||
if (chatKey.text == "") {
|
||||
ensUsername.text = "";
|
||||
pubKey = "";
|
||||
|
@ -113,6 +117,7 @@ Item {
|
|||
//% "Can't chat with yourself"
|
||||
root.validationError = qsTrId("can-t-chat-with-yourself");
|
||||
} else {
|
||||
chatKey.hasValidSearchResult = true
|
||||
searchResults.username = Utils.addStatusEns(chatKey.text.trim())
|
||||
let userAlias = globalUtils.generateAlias(resolvedPubKey)
|
||||
userAlias = userAlias.length > 20 ? userAlias.substring(0, 19) + "..." : userAlias
|
||||
|
@ -151,6 +156,7 @@ Item {
|
|||
noContactsRect.visible = false;
|
||||
searchResults.loading = false;
|
||||
root.validationError = "";
|
||||
chatKey.hasValidSearchResult = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -199,6 +205,7 @@ Item {
|
|||
}
|
||||
root.pubKeys = pubKeysCopy
|
||||
|
||||
chatKey.hasValidSearchResult = false
|
||||
userClicked(contact.pubKey, contact.isContact, contact.name, contact.address)
|
||||
}
|
||||
expanded: !searchResults.loading && pubKey === "" && !searchResults.showProfileNotFoundMessage
|
||||
|
@ -217,6 +224,7 @@ Item {
|
|||
if (!validate()) {
|
||||
return
|
||||
}
|
||||
chatKey.hasValidSearchResult = false
|
||||
userClicked(pubKey, isAddedContact, username, searchResults.address)
|
||||
}
|
||||
onAddToContactsButtonClicked: {
|
||||
|
|
|
@ -11,8 +11,8 @@ import StatusQ.Controls 0.1
|
|||
Item {
|
||||
id: root
|
||||
width: parent.width
|
||||
height: Style.current.smallPadding + prioritytext.height +
|
||||
(advancedMode ? advancedModeItemGroup.height : selectorButtons.height)
|
||||
height: visible ? Style.current.smallPadding + prioritytext.height +
|
||||
(advancedMode ? advancedModeItemGroup.height : selectorButtons.height) : 0
|
||||
|
||||
property double gasPrice: 0
|
||||
|
||||
|
@ -36,6 +36,7 @@ Item {
|
|||
property alias selectedGasPrice: inputGasPrice.text
|
||||
property alias selectedGasLimit: inputGasLimit.text
|
||||
property string defaultGasLimit: "0"
|
||||
property string maxFiatFees: selectedGasFiatValue + root.defaultCurrency.toUpperCase()
|
||||
|
||||
|
||||
property alias selectedTipLimit: inputPerGasTipLimit.text
|
||||
|
@ -441,7 +442,7 @@ Item {
|
|||
|
||||
StyledText {
|
||||
id: maxPriorityFeeFiatText
|
||||
text: `${selectedGasFiatValue} ${root.defaultCurrency.toUpperCase()}`
|
||||
text: root.maxFiatFees
|
||||
anchors.verticalCenter: maxPriorityFeeText.verticalCenter
|
||||
anchors.left: maxPriorityFeeText.right
|
||||
anchors.leftMargin: 6
|
||||
|
|
|
@ -2,32 +2,32 @@ import QtQuick 2.13
|
|||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
import QtQuick.Dialogs 1.3
|
||||
import QtGraphicalEffects 1.0
|
||||
import StatusQ.Controls.Validators 0.1
|
||||
|
||||
import utils 1.0
|
||||
import shared.stores 1.0
|
||||
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Popups 0.1
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
||||
import "../panels"
|
||||
import "../controls"
|
||||
import "../views"
|
||||
import "."
|
||||
|
||||
// TODO: replace with StatusModal
|
||||
ModalPopup {
|
||||
id: root
|
||||
property var contactsStore
|
||||
StatusModal {
|
||||
id: popup
|
||||
|
||||
property alias selectFromAccount: selectFromAccount
|
||||
property alias selectRecipient: selectRecipient
|
||||
property alias stack: stack
|
||||
|
||||
property var store
|
||||
property bool isContact: false
|
||||
|
||||
//% "Send"
|
||||
title: qsTrId("command-button-send")
|
||||
height: 540
|
||||
|
||||
property var contactsStore
|
||||
property var preSelectedAccount
|
||||
property var preSelectedRecipient
|
||||
property bool launchedFromChat: false
|
||||
property MessageDialog sendingError: MessageDialog {
|
||||
id: sendingError
|
||||
//% "Error sending the transaction"
|
||||
|
@ -39,11 +39,11 @@ ModalPopup {
|
|||
function sendTransaction() {
|
||||
stack.currentGroup.isPending = true
|
||||
let success = false
|
||||
if(txtAmount.selectedAsset.address === "" || txtAmount.selectedAsset.address === Constants.zeroAddress){
|
||||
success = root.store.transferEth(
|
||||
selectFromAccount.selectedAccount.address,
|
||||
selectRecipient.selectedRecipient.address,
|
||||
txtAmount.selectedAmount,
|
||||
if(advancedHeader.assetSelector.selectedAsset.address === "" || advancedHeader.assetSelector.selectedAsset.address === Constants.zeroAddress){
|
||||
success = popup.store.transferEth(
|
||||
advancedHeader.accountSelector.selectedAccount.address,
|
||||
advancedHeader.recipientSelector.selectedRecipient.address,
|
||||
advancedHeader.amountToSendInput.text,
|
||||
gasSelector.selectedGasLimit,
|
||||
gasSelector.isEIP1559Enabled ? "" : gasSelector.selectedGasPrice,
|
||||
gasSelector.selectedTipLimit,
|
||||
|
@ -51,11 +51,11 @@ ModalPopup {
|
|||
transactionSigner.enteredPassword,
|
||||
stack.uuid)
|
||||
} else {
|
||||
success = root.store.transferTokens(
|
||||
selectFromAccount.selectedAccount.address,
|
||||
selectRecipient.selectedRecipient.address,
|
||||
txtAmount.selectedAsset.address,
|
||||
txtAmount.selectedAmount,
|
||||
success = popup.store.transferTokens(
|
||||
advancedHeader.accountSelector.selectedAccount.address,
|
||||
advancedHeader.recipientSelector.selectedRecipient.address,
|
||||
advancedHeader.assetSelector.selectedAsset.address,
|
||||
advancedHeader.amountToSendInput.text,
|
||||
gasSelector.selectedGasLimit,
|
||||
gasSelector.isEIP1559Enabled ? "" : gasSelector.selectedGasPrice,
|
||||
gasSelector.selectedTipLimit,
|
||||
|
@ -63,291 +63,246 @@ ModalPopup {
|
|||
transactionSigner.enteredPassword,
|
||||
stack.uuid)
|
||||
}
|
||||
|
||||
// Till the method is moved to thread this is handled by a signal to which connection is made in the end of the file
|
||||
// if(!success){
|
||||
// //% "Invalid transaction parameters"
|
||||
// sendingError.text = qsTrId("invalid-transaction-parameters")
|
||||
// sendingError.open()
|
||||
// }
|
||||
}
|
||||
|
||||
TransactionStackView {
|
||||
id: stack
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Style.current.padding
|
||||
anchors.rightMargin: Style.current.padding
|
||||
onGroupActivated: {
|
||||
root.title = group.headerText
|
||||
btnNext.text = group.footerText
|
||||
width: 556
|
||||
// To-Do as per design once the account selector become floating the heigth can be as defined in design as 595
|
||||
height: 670
|
||||
showHeader: false
|
||||
showFooter: false
|
||||
showAdvancedFooter: !!popup.advancedHeader ? popup.advancedHeader.isReady && gasValidator.isValid : false
|
||||
showAdvancedHeader: true
|
||||
|
||||
onOpened: {
|
||||
if(!!advancedHeader) {
|
||||
advancedHeader.amountToSendInput.input.edit.forceActiveFocus()
|
||||
|
||||
if(popup.launchedFromChat) {
|
||||
advancedHeader.recipientSelector.selectedType = RecipientSelector.Type.Contact
|
||||
advancedHeader.recipientSelector.readOnly = true
|
||||
advancedHeader.recipientSelector.selectedRecipient = popup.preSelectedRecipient
|
||||
}
|
||||
if(popup.preSelectedAccount) {
|
||||
advancedHeader.accountSelector.selectedAccount = popup.preSelectedAccount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
advancedHeaderComponent: SendModalHeader {
|
||||
store: popup.store
|
||||
contactsStore: popup.contactsStore
|
||||
estimateGas: function() {
|
||||
if(popup.contentItem.currentGroup.isValid)
|
||||
gasSelector.estimateGas()
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: TransactionStackView {
|
||||
id: stack
|
||||
property alias currentGroup: stack.currentGroup
|
||||
anchors.leftMargin: Style.current.xlPadding
|
||||
anchors.topMargin: (!!advancedHeader ? advancedHeader.height: 0) + Style.current.smallPadding
|
||||
anchors.rightMargin: Style.current.xlPadding
|
||||
anchors.bottomMargin: popup.showAdvancedFooter && !!advancedFooter ? advancedFooter.height : Style.current.padding
|
||||
TransactionFormGroup {
|
||||
id: group1
|
||||
//% "Send"
|
||||
headerText: qsTrId("command-button-send")
|
||||
//% "Continue"
|
||||
footerText: qsTrId("continue")
|
||||
anchors.fill: parent
|
||||
ScrollView {
|
||||
height: stack.height
|
||||
width: parent.width
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
|
||||
StatusAccountSelector {
|
||||
id: selectFromAccount
|
||||
accounts: root.store.accounts
|
||||
selectedAccount: {
|
||||
const currAcc = root.store.currentAccount
|
||||
if (currAcc.walletType !== Constants.watchWalletType) {
|
||||
return currAcc
|
||||
ScrollBar.vertical.policy: ScrollBar.AlwaysOff
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
contentHeight: addressSelector.height + networkAndFeesSelector.height + gasSelector.height + gasValidator.height
|
||||
clip: true
|
||||
|
||||
TabAddressSelectorView {
|
||||
id: addressSelector
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
store: popup.store
|
||||
onContactSelected: {
|
||||
if(!!popup.advancedHeader)
|
||||
advancedHeader.recipientSelector.input.text = address
|
||||
}
|
||||
return null
|
||||
}
|
||||
currency: root.store.currentCurrency
|
||||
width: stack.width
|
||||
//% "From account"
|
||||
label: qsTrId("from-account")
|
||||
onSelectedAccountChanged: if (isValid) { gasSelector.estimateGas() }
|
||||
}
|
||||
SeparatorWithIcon {
|
||||
id: separator
|
||||
anchors.top: selectFromAccount.bottom
|
||||
anchors.topMargin: 19
|
||||
}
|
||||
RecipientSelector {
|
||||
id: selectRecipient
|
||||
accounts: root.store.accounts
|
||||
contactsStore: root.contactsStore
|
||||
//% "Recipient"
|
||||
label: qsTrId("recipient")
|
||||
anchors.top: separator.bottom
|
||||
anchors.topMargin: 10
|
||||
width: stack.width
|
||||
onSelectedRecipientChanged: if (isValid) { gasSelector.estimateGas() }
|
||||
}
|
||||
}
|
||||
TransactionFormGroup {
|
||||
id: group2
|
||||
//% "Send"
|
||||
headerText: qsTrId("command-button-send")
|
||||
//% "Preview"
|
||||
footerText: qsTr("Continue")
|
||||
|
||||
AssetAndAmountInput {
|
||||
id: txtAmount
|
||||
selectedAccount: selectFromAccount.selectedAccount
|
||||
currentCurrency: root.store.currentCurrency
|
||||
// TODO make those use a debounce
|
||||
getFiatValue: root.store.getFiatValue
|
||||
// getCryptoValue: RootStore.cryptoValue
|
||||
width: stack.width
|
||||
onSelectedAssetChanged: if (isValid) { gasSelector.estimateGas() }
|
||||
onSelectedAmountChanged: if (isValid) { gasSelector.estimateGas() }
|
||||
}
|
||||
GasSelector {
|
||||
id: gasSelector
|
||||
anchors.top: txtAmount.bottom
|
||||
anchors.topMargin: Style.current.padding
|
||||
gasPrice: parseFloat(root.store.gasPrice)
|
||||
getGasEthValue: root.store.getGasEthValue
|
||||
getFiatValue: root.store.getFiatValue
|
||||
defaultCurrency: root.store.currentCurrency
|
||||
isEIP1559Enabled: root.store.isEIP1559Enabled()
|
||||
latestBaseFeePerGas: root.store.latestBaseFeePerGas()
|
||||
suggestedFees: root.store.suggestedFees()
|
||||
TabNetworkAndFees {
|
||||
id: networkAndFeesSelector
|
||||
anchors.top: addressSelector.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
visible: popup.store.isMultiNetworkEnabled
|
||||
}
|
||||
|
||||
width: stack.width
|
||||
property var estimateGas: Backpressure.debounce(gasSelector, 600, function() {
|
||||
if (!(selectFromAccount.selectedAccount && selectFromAccount.selectedAccount.address &&
|
||||
selectRecipient.selectedRecipient && selectRecipient.selectedRecipient.address &&
|
||||
txtAmount.selectedAsset && txtAmount.selectedAsset.address &&
|
||||
txtAmount.selectedAmount)) {
|
||||
selectedGasLimit = 250000
|
||||
GasSelector {
|
||||
id: gasSelector
|
||||
anchors.top: networkAndFeesSelector.visible ? networkAndFeesSelector.bottom : addressSelector.bottom
|
||||
gasPrice: parseFloat(popup.store.gasPrice)
|
||||
getGasEthValue: popup.store.getGasEthValue
|
||||
getFiatValue: popup.store.getFiatValue
|
||||
defaultCurrency: popup.store.currentCurrency
|
||||
isEIP1559Enabled: popup.store.isEIP1559Enabled()
|
||||
latestBaseFeePerGas: popup.store.latestBaseFeePerGas()
|
||||
suggestedFees: popup.store.suggestedFees()
|
||||
|
||||
visible: !popup.store.isMultiNetworkEnabled
|
||||
|
||||
width: stack.width
|
||||
property var estimateGas: Backpressure.debounce(gasSelector, 600, function() {
|
||||
if (!(advancedHeader.accountSelector.selectedAccount && advancedHeader.accountSelector.selectedAccount.address &&
|
||||
advancedHeader.recipientSelector.selectedRecipient && advancedHeader.recipientSelector.selectedRecipient.address &&
|
||||
advancedHeader.assetSelector.selectedAsset && advancedHeader.assetSelector.selectedAsset.address &&
|
||||
advancedHeader.amountToSendInput.text)) {
|
||||
selectedGasLimit = 250000
|
||||
defaultGasLimit = selectedGasLimit
|
||||
return
|
||||
}
|
||||
|
||||
let gasEstimate = JSON.parse(popup.store.estimateGas(
|
||||
advancedHeader.accountSelector.selectedAccount.address,
|
||||
advancedHeader.recipientSelector.selectedRecipient.address,
|
||||
advancedHeader.assetSelector.selectedAsset.address,
|
||||
advancedHeader.amountToSendInput.text,
|
||||
""))
|
||||
|
||||
if (!gasEstimate.success) {
|
||||
//% "Error estimating gas: %1"
|
||||
console.warn(qsTrId("error-estimating-gas---1").arg(gasEstimate.error.message))
|
||||
return
|
||||
}
|
||||
|
||||
selectedGasLimit = gasEstimate.result
|
||||
defaultGasLimit = selectedGasLimit
|
||||
return
|
||||
}
|
||||
|
||||
let gasEstimate = JSON.parse(root.store.estimateGas(
|
||||
selectFromAccount.selectedAccount.address,
|
||||
selectRecipient.selectedRecipient.address,
|
||||
txtAmount.selectedAsset.address,
|
||||
txtAmount.selectedAmount,
|
||||
""))
|
||||
|
||||
if (!gasEstimate.success) {
|
||||
//% "Error estimating gas: %1"
|
||||
console.warn(qsTrId("error-estimating-gas---1").arg(gasEstimate.error.message))
|
||||
return
|
||||
}
|
||||
|
||||
selectedGasLimit = gasEstimate.result
|
||||
defaultGasLimit = selectedGasLimit
|
||||
})
|
||||
}
|
||||
GasValidator {
|
||||
id: gasValidator
|
||||
anchors.top: gasSelector.bottom
|
||||
selectedAccount: selectFromAccount.selectedAccount
|
||||
selectedAmount: parseFloat(txtAmount.selectedAmount)
|
||||
selectedAsset: txtAmount.selectedAsset
|
||||
selectedGasEthValue: gasSelector.selectedGasEthValue
|
||||
}
|
||||
}
|
||||
TransactionFormGroup {
|
||||
id: group3
|
||||
//% "Transaction preview"
|
||||
headerText: qsTrId("transaction-preview")
|
||||
//% "Sign with password"
|
||||
footerText: qsTrId("sign-with-password")
|
||||
|
||||
TransactionPreview {
|
||||
id: pvwTransaction
|
||||
width: stack.width
|
||||
fromAccount: selectFromAccount.selectedAccount
|
||||
gas: {
|
||||
"value": gasSelector.selectedGasEthValue,
|
||||
"symbol": "ETH",
|
||||
"fiatValue": gasSelector.selectedGasFiatValue
|
||||
})
|
||||
}
|
||||
GasValidator {
|
||||
id: gasValidator
|
||||
anchors.top: gasSelector.bottom
|
||||
selectedAccount: advancedHeader.accountSelector.selectedAccount
|
||||
selectedAmount: parseFloat(advancedHeader.amountToSendInput.text)
|
||||
selectedAsset: advancedHeader.assetSelector.selectedAsset
|
||||
selectedGasEthValue: gasSelector.selectedGasEthValue
|
||||
}
|
||||
toAccount: selectRecipient.selectedRecipient
|
||||
asset: txtAmount.selectedAsset
|
||||
amount: { "value": txtAmount.selectedAmount, "fiatValue": txtAmount.selectedFiatAmount }
|
||||
currency: root.store.currentCurrency
|
||||
}
|
||||
SendToContractWarning {
|
||||
id: sendToContractWarning
|
||||
anchors.top: pvwTransaction.bottom
|
||||
selectedRecipient: selectRecipient.selectedRecipient
|
||||
}
|
||||
}
|
||||
TransactionFormGroup {
|
||||
id: group4
|
||||
//% "Sign with password"
|
||||
headerText: qsTrId("sign-with-password")
|
||||
//% "Send %1 %2"
|
||||
footerText: qsTrId("send--1--2").arg(txtAmount.selectedAmount).arg(!!txtAmount.selectedAsset ? txtAmount.selectedAsset.symbol : "")
|
||||
|
||||
StackView.onActivated: {
|
||||
transactionSigner.forceActiveFocus(Qt.MouseFocusReason)
|
||||
}
|
||||
|
||||
TransactionSigner {
|
||||
id: transactionSigner
|
||||
Layout.topMargin: Style.current.smallPadding
|
||||
width: stack.width
|
||||
signingPhrase: root.store.signingPhrase
|
||||
signingPhrase: popup.store.signingPhrase
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer: Item {
|
||||
width: parent.width
|
||||
height: btnNext.height
|
||||
|
||||
StatusRoundButton {
|
||||
id: btnBack
|
||||
anchors.left: parent.left
|
||||
visible: !stack.isFirstGroup
|
||||
icon.name: "arrow-right"
|
||||
icon.width: 20
|
||||
icon.height: 16
|
||||
icon.rotation: 180
|
||||
onClicked: {
|
||||
stack.back()
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: transactionSettingsConfirmationPopupComponent
|
||||
TransactionSettingsConfirmationPopup {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
StatusButton {
|
||||
id: btnNext
|
||||
anchors.right: parent.right
|
||||
//% "Next"
|
||||
text: qsTrId("next")
|
||||
enabled: stack.currentGroup.isValid && !stack.currentGroup.isPending
|
||||
loading: stack.currentGroup.isPending
|
||||
onClicked: {
|
||||
const validity = stack.currentGroup.validate()
|
||||
if (validity.isValid && !validity.isPending) {
|
||||
if (stack.isLastGroup) {
|
||||
return root.sendTransaction()
|
||||
}
|
||||
|
||||
if(gasSelector.isEIP1559Enabled && stack.currentGroup === group2 && gasSelector.advancedMode){
|
||||
if(gasSelector.showPriceLimitWarning || gasSelector.showTipLimitWarning){
|
||||
Global.openPopup(transactionSettingsConfirmationPopupComponent, {
|
||||
currentBaseFee: gasSelector.latestBaseFeePerGasGwei,
|
||||
currentMinimumTip: gasSelector.perGasTipLimitFloor,
|
||||
currentAverageTip: gasSelector.perGasTipLimitAverage,
|
||||
tipLimit: gasSelector.selectedTipLimit,
|
||||
suggestedTipLimit: gasSelector.perGasTipLimitFloor,
|
||||
priceLimit: gasSelector.selectedOverallLimit,
|
||||
suggestedPriceLimit: gasSelector.latestBaseFeePerGasGwei + gasSelector.perGasTipLimitFloor,
|
||||
showPriceLimitWarning: gasSelector.showPriceLimitWarning,
|
||||
showTipLimitWarning: gasSelector.showTipLimitWarning,
|
||||
onConfirm: function(){
|
||||
stack.next();
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
stack.next()
|
||||
advancedFooterComponent: SendModalFooter {
|
||||
maxFiatFees: gasSelector.maxFiatFees
|
||||
currentGroupPending: popup.contentItem.currentGroup.isPending
|
||||
currentGroupValid: popup.contentItem.currentGroup.isValid
|
||||
isLastGroup: popup.contentItem.isLastGroup
|
||||
onNextButtonClicked: {
|
||||
const validity = popup.contentItem.currentGroup.validate()
|
||||
if (validity.isValid && !validity.isPending) {
|
||||
if (popup.contentItem.isLastGroup) {
|
||||
return popup.sendTransaction()
|
||||
}
|
||||
|
||||
if(gasSelector.isEIP1559Enabled && popup.contentItem.currentGroup === group1 && gasSelector.advancedMode){
|
||||
if(gasSelector.showPriceLimitWarning || gasSelector.showTipLimitWarning){
|
||||
Global.openPopup(transactionSettingsConfirmationPopupComponent, {
|
||||
currentBaseFee: gasSelector.latestBaseFeePerGasGwei,
|
||||
currentMinimumTip: gasSelector.perGasTipLimitFloor,
|
||||
currentAverageTip: gasSelector.perGasTipLimitAverage,
|
||||
tipLimit: gasSelector.selectedTipLimit,
|
||||
suggestedTipLimit: gasSelector.perGasTipLimitFloor,
|
||||
priceLimit: gasSelector.selectedOverallLimit,
|
||||
suggestedPriceLimit: gasSelector.latestBaseFeePerGasGwei + gasSelector.perGasTipLimitFloor,
|
||||
showPriceLimitWarning: gasSelector.showPriceLimitWarning,
|
||||
showTipLimitWarning: gasSelector.showTipLimitWarning,
|
||||
onConfirm: function(){
|
||||
popup.contentItem.next();
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
popup.contentItem.next()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: transactionSettingsConfirmationPopupComponent
|
||||
TransactionSettingsConfirmationPopup {}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: advancedHeader
|
||||
onIsReadyChanged: {
|
||||
if(!advancedHeader.isReady && popup.contentItem.isLastGroup)
|
||||
popup.contentItem.back()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: popup.store.walletSectionTransactionsInst
|
||||
onTransactionSent: {
|
||||
try {
|
||||
let response = JSON.parse(txResult)
|
||||
if (response.uuid !== stack.uuid) return
|
||||
|
||||
stack.currentGroup.isPending = false
|
||||
|
||||
if (!response.success) {
|
||||
if (Utils.isInvalidPasswordMessage(response.result)){
|
||||
//% "Wrong password"
|
||||
transactionSigner.validationError = qsTrId("wrong-password")
|
||||
return
|
||||
}
|
||||
sendingError.text = response.result
|
||||
return sendingError.open()
|
||||
}
|
||||
|
||||
// % "Transaction pending..."
|
||||
Global.toastMessage.title = qsTrId("ens-transaction-pending")
|
||||
Global.toastMessage.source = Style.svg("loading")
|
||||
Global.toastMessage.iconColor = Style.current.primary
|
||||
Global.toastMessage.iconRotates = true
|
||||
Global.toastMessage.link = `${popup.store.getEtherscanLink()}/${response.result}`
|
||||
Global.toastMessage.open()
|
||||
popup.close()
|
||||
} catch (e) {
|
||||
console.error('Error parsing the response', e)
|
||||
}
|
||||
}
|
||||
// Not Refactored Yet
|
||||
Connections {
|
||||
target: root.store.walletSectionTransactionsInst
|
||||
onTransactionSent: {
|
||||
try {
|
||||
let response = JSON.parse(txResult)
|
||||
if (response.uuid !== stack.uuid) return
|
||||
|
||||
stack.currentGroup.isPending = false
|
||||
|
||||
if (!response.success) {
|
||||
if (Utils.isInvalidPasswordMessage(response.result)){
|
||||
//% "Wrong password"
|
||||
transactionSigner.validationError = qsTrId("wrong-password")
|
||||
return
|
||||
}
|
||||
sendingError.text = response.result
|
||||
return sendingError.open()
|
||||
}
|
||||
|
||||
// % "Transaction pending..."
|
||||
Global.toastMessage.title = qsTrId("ens-transaction-pending")
|
||||
Global.toastMessage.source = Style.svg("loading")
|
||||
Global.toastMessage.iconColor = Style.current.primary
|
||||
Global.toastMessage.iconRotates = true
|
||||
// Refactor this
|
||||
// Global.toastMessage.link = `${walletModel.utilsView.etherscanLink}/${response.result}`
|
||||
Global.toastMessage.open()
|
||||
root.close()
|
||||
} catch (e) {
|
||||
console.error('Error parsing the response', e)
|
||||
}
|
||||
}
|
||||
// onTransactionCompleted: {
|
||||
// if (success) {
|
||||
// //% "Transaction completed"
|
||||
// Global.toastMessage.title = qsTrId("transaction-completed")
|
||||
// Global.toastMessage.source = Style.svg("check-circle")
|
||||
// Global.toastMessage.iconColor = Style.current.success
|
||||
// } else {
|
||||
// //% "Transaction failed"
|
||||
// Global.toastMessage.title = qsTrId("ens-registration-failed-title")
|
||||
// Global.toastMessage.source = Style.svg("block-icon")
|
||||
// Global.toastMessage.iconColor = Style.current.danger
|
||||
// }
|
||||
// Global.toastMessage.link = `${walletModel.utilsView.etherscanLink}/${txHash}`
|
||||
// Global.toastMessage.open()
|
||||
// }
|
||||
}
|
||||
// onTransactionCompleted: {
|
||||
// if (success) {
|
||||
// //% "Transaction completed"
|
||||
// Global.toastMessage.title = qsTrId("transaction-completed")
|
||||
// Global.toastMessage.source = Style.svg("check-circle")
|
||||
// Global.toastMessage.iconColor = Style.current.success
|
||||
// } else {
|
||||
// //% "Transaction failed"
|
||||
// Global.toastMessage.title = qsTrId("ens-registration-failed-title")
|
||||
// Global.toastMessage.source = Style.svg("block-icon")
|
||||
// Global.toastMessage.iconColor = Style.current.danger
|
||||
// }
|
||||
// Global.toastMessage.link = `${walletModel.utilsView.etherscanLink}/${txHash}`
|
||||
// Global.toastMessage.open()
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
/*##^##
|
||||
Designer {
|
||||
D{i:0;autoSize:true;height:480;width:640}
|
||||
}
|
||||
##^##*/
|
||||
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
|
||||
import utils 1.0
|
||||
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Popups 0.1
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
||||
Rectangle {
|
||||
id: footer
|
||||
|
||||
//% "Unknown"
|
||||
property string estimatedTime: qsTr("Unknown")
|
||||
property string maxFiatFees: ""
|
||||
property bool currentGroupPending: true
|
||||
property bool currentGroupValid: false
|
||||
property bool isLastGroup: false
|
||||
|
||||
signal nextButtonClicked()
|
||||
|
||||
width: parent.width
|
||||
height: 82
|
||||
radius: 8
|
||||
color: Theme.palette.statusModal.backgroundColor
|
||||
|
||||
Rectangle {
|
||||
anchors.top: parent.top
|
||||
width: parent.width
|
||||
height: parent.radius
|
||||
color: parent.color
|
||||
|
||||
StatusModalDivider {
|
||||
anchors.top: parent.top
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: 32
|
||||
anchors.rightMargin: 32
|
||||
|
||||
ColumnLayout {
|
||||
StatusBaseText {
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.directColor5
|
||||
//% "Estimated Time:"
|
||||
text: qsTr("Estimated Time:")
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
// To-do not implemented yet
|
||||
StatusBaseText {
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.directColor1
|
||||
text: estimatedTime
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
|
||||
// To fill gap in between
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: 1
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: 16
|
||||
ColumnLayout {
|
||||
StatusBaseText {
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.directColor5
|
||||
//% "Max Fees:"
|
||||
text: qsTr("Max Fees:")
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
StatusBaseText {
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.directColor1
|
||||
text: maxFiatFees
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
|
||||
StatusFlatButton {
|
||||
icon.name: isLastGroup ? "" : "password"
|
||||
//% "Send"
|
||||
text: qsTrId("command-button-send")
|
||||
size: StatusBaseButton.Size.Large
|
||||
normalColor: Theme.palette.primaryColor2
|
||||
disaledColor: Theme.palette.baseColor2
|
||||
enabled: currentGroupValid && !currentGroupPending
|
||||
loading: currentGroupPending
|
||||
onClicked: nextButtonClicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
import StatusQ.Controls.Validators 0.1
|
||||
|
||||
import utils 1.0
|
||||
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Popups 0.1
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
||||
import "../controls"
|
||||
import "../views"
|
||||
|
||||
Rectangle {
|
||||
id: header
|
||||
|
||||
property alias accountSelector: accountSelector
|
||||
property alias recipientSelector: recipientSelector
|
||||
property alias amountToSendInput: amountToSendInput
|
||||
property alias assetSelector: assetSelector
|
||||
|
||||
property var store
|
||||
property var contactsStore
|
||||
property var estimateGas: function() {}
|
||||
property bool isReady: amountToSendInput.valid && !amountToSendInput.pending && recipientSelector.isValid && !recipientSelector.isPending
|
||||
|
||||
QtObject {
|
||||
id: _internal
|
||||
property string maxFiatBalance: Utils.stripTrailingZeros(parseFloat(assetSelector.selectedAsset.balance).toFixed(4))
|
||||
//% "Please enter a valid amount"
|
||||
property string sendAmountInputErrorMessage: qsTr("Please enter a valid amount")
|
||||
//% "Max:"
|
||||
property string maxString: qsTr("Max: ")
|
||||
}
|
||||
|
||||
radius: 8
|
||||
|
||||
color: Theme.palette.statusModal.backgroundColor
|
||||
width: parent.width
|
||||
height: headerLayout.height + Style.current.xlPadding + (!!recipientSelector.input.text && recipientSelector.input.hasValidSearchResult ? 70 : 0)
|
||||
|
||||
Rectangle {
|
||||
id: border
|
||||
anchors.bottom: parent.bottom
|
||||
width: parent.width
|
||||
height: parent.radius
|
||||
color: parent.color
|
||||
|
||||
StatusModalDivider {
|
||||
anchors.bottom: parent.bottom
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: headerLayout
|
||||
spacing: 8
|
||||
width: parent.width
|
||||
anchors.top: parent.top
|
||||
anchors.leftMargin: Style.current.xlPadding
|
||||
anchors.rightMargin: Style.current.xlPadding
|
||||
anchors.topMargin: Style.current.padding
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
StatusAccountSelector {
|
||||
id: accountSelector
|
||||
accounts: header.store.accounts
|
||||
selectedAccount: {
|
||||
const currAcc = header.store.currentAccount
|
||||
if (currAcc.walletType !== Constants.watchWalletType) {
|
||||
return currAcc
|
||||
}
|
||||
return null
|
||||
}
|
||||
currency: header.store.currentCurrency
|
||||
width: parent.width
|
||||
label: ""
|
||||
onSelectedAccountChanged: {
|
||||
assetSelector.assets = Qt.binding(function() {
|
||||
if (selectedAccount) {
|
||||
return selectedAccount.assets
|
||||
}
|
||||
})
|
||||
if (isValid) { estimateGas() }
|
||||
}
|
||||
showAccountDetails: false
|
||||
selectField.select.height: 32
|
||||
}
|
||||
ColumnLayout {
|
||||
id: assetAndAmmountSelector
|
||||
RowLayout {
|
||||
spacing: 16
|
||||
StatusBaseText {
|
||||
//% "Send"
|
||||
text: qsTrId("command-button-send")
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.directColor1
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
StatusListItemTag {
|
||||
//% "No balances active"
|
||||
title: assetSelector.selectedAsset.balance > 0 ? _internal.maxString + (assetSelector.selectedAsset ? _internal.maxFiatBalance : "0.00") : qsTr("No balances active")
|
||||
closeButtonVisible: false
|
||||
titleText.font.pixelSize: 12
|
||||
height: 22
|
||||
}
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: childrenRect.height
|
||||
AmountInputWithCursor {
|
||||
id: amountToSendInput
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
width: parent.width - assetSelector.width
|
||||
input.placeholderText: "0.00" + " " + assetSelector.selectedAsset.symbol
|
||||
errorMessageCmp.anchors.rightMargin: -100
|
||||
validators: [
|
||||
StatusFloatValidator{
|
||||
id: floatValidator
|
||||
bottom: 0
|
||||
top: _internal.maxFiatBalance
|
||||
errorMessage: _internal.sendAmountInputErrorMessage
|
||||
}
|
||||
]
|
||||
Keys.onReleased: {
|
||||
let amount = amountToSendInput.text.trim()
|
||||
|
||||
if (isNaN(amount)) {
|
||||
return
|
||||
}
|
||||
if (amount === "") {
|
||||
txtFiatBalance.text = "0.00"
|
||||
} else {
|
||||
txtFiatBalance.text = header.store.getFiatValue(amount, assetSelector.selectedAsset.symbol, header.store.currentCurrency)
|
||||
}
|
||||
estimateGas()
|
||||
}
|
||||
}
|
||||
StatusAssetSelector {
|
||||
id: assetSelector
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
defaultToken: Style.png("tokens/DEFAULT-TOKEN@3x")
|
||||
getCurrencyBalanceString: function (currencyBalance) {
|
||||
return Utils.toLocaleString(currencyBalance.toFixed(2), header.store.locale, {"currency": true}) + " " + header.store.currentCurrency.toUpperCase()
|
||||
}
|
||||
tokenAssetSourceFn: function (symbol) {
|
||||
return symbol ? Style.png("tokens/" + symbol) : defaultToken
|
||||
}
|
||||
onSelectedAssetChanged: {
|
||||
if (!assetSelector.selectedAsset) {
|
||||
return
|
||||
}
|
||||
if (amountToSendInput.text === "" || isNaN(amountToSendInput.text)) {
|
||||
return
|
||||
}
|
||||
txtFiatBalance.text = header.store.getFiatValue(amountToSendInput.text, assetSelector.selectedAsset.symbol, header.store.currentCurrency)
|
||||
estimateGas()
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
StyledTextField {
|
||||
id: txtFiatBalance
|
||||
color: txtFiatBalance.activeFocus ? Style.current.textColor : Style.current.secondaryText
|
||||
font.weight: Font.Medium
|
||||
font.pixelSize: 12
|
||||
inputMethodHints: Qt.ImhFormattedNumbersOnly
|
||||
text: "0.00"
|
||||
selectByMouse: true
|
||||
background: Rectangle {
|
||||
color: Style.current.transparent
|
||||
}
|
||||
padding: 0
|
||||
Keys.onReleased: {
|
||||
let balance = txtFiatBalance.text.trim()
|
||||
if (balance === "" || isNaN(balance)) {
|
||||
return
|
||||
}
|
||||
// To-Do Not refactored yet
|
||||
// amountToSendInput.text = root.getCryptoValue(balance, header.store.currentCurrency, assetSelector.selectedAsset.symbol)
|
||||
}
|
||||
}
|
||||
StatusBaseText {
|
||||
id: currencyText
|
||||
text: header.store.currentCurrency.toUpperCase()
|
||||
font.pixelSize: 13
|
||||
color: Theme.palette.directColor5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// To-do use standard StatusInput component once the flow for ens name resolution is clear
|
||||
RecipientSelector {
|
||||
id: recipientSelector
|
||||
accounts: header.store.accounts
|
||||
contactsStore: header.contactsStore
|
||||
//% To
|
||||
label: qsTr("To")
|
||||
Layout.fillWidth: true
|
||||
//% "Enter an ENS name or address"
|
||||
input.placeholderText: qsTr("Enter an ENS name or address")
|
||||
input.anchors.leftMargin: 0
|
||||
input.anchors.rightMargin: 0
|
||||
labelFont.pixelSize: 15
|
||||
labelFont.weight: Font.Normal
|
||||
input.implicitHeight: 56
|
||||
isSelectorVisible: false
|
||||
addContactEnabled: false
|
||||
onSelectedRecipientChanged: estimateGas()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,216 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
import QtQuick.Dialogs 1.3
|
||||
|
||||
import utils 1.0
|
||||
import shared.stores 1.0
|
||||
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Popups 0.1
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
||||
import "../panels"
|
||||
import "../controls"
|
||||
import "../views"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
clip: true
|
||||
height: accountSelectionTabBar.height + stackLayout.height + Style.current.xlPadding
|
||||
|
||||
property var store
|
||||
|
||||
signal contactSelected(string address, int type)
|
||||
|
||||
TabBar {
|
||||
id: accountSelectionTabBar
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 20
|
||||
width: parent.width
|
||||
height: assetBtn.height
|
||||
background: Rectangle {
|
||||
color: Style.current.transparent
|
||||
}
|
||||
StatusTabButton {
|
||||
id: assetBtn
|
||||
//% "Saved"
|
||||
btnText: qsTr("Saved")
|
||||
}
|
||||
StatusTabButton {
|
||||
id: collectiblesBtn
|
||||
anchors.left: assetBtn.right
|
||||
anchors.leftMargin: 32
|
||||
//% "My Accounts"
|
||||
btnText: qsTr("My Accounts")
|
||||
}
|
||||
StatusTabButton {
|
||||
id: historyBtn
|
||||
anchors.left: collectiblesBtn.right
|
||||
anchors.leftMargin: 32
|
||||
//% "Recent"
|
||||
btnText: qsTr("Recent")
|
||||
}
|
||||
}
|
||||
|
||||
StackLayout {
|
||||
id: stackLayout
|
||||
anchors.top: accountSelectionTabBar.bottom
|
||||
height: stackLayout.childrenRect.height
|
||||
width: parent.width
|
||||
currentIndex: accountSelectionTabBar.currentIndex
|
||||
|
||||
// To-do adapt to new design and make block white/balck once the list items etc support new color scheme
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
height: savedAddresses.height
|
||||
color: "transparent"
|
||||
radius: 8
|
||||
|
||||
ListView {
|
||||
id: savedAddresses
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
width: parent.width
|
||||
height: Math.min(288, savedAddresses.contentHeight)
|
||||
|
||||
model: root.store.savedAddressesModel
|
||||
clip: true
|
||||
header: savedAddresses.count > 0 ? search : nothingInList
|
||||
headerPositioning: ListView.OverlayHeader
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
delegate: StatusListItem {
|
||||
id: savedAddress
|
||||
width: visible ? parent.width: 0
|
||||
height: visible ? 64 : 0
|
||||
title: name
|
||||
subTitle: address
|
||||
radius: 0
|
||||
visible: !savedAddresses.headerItem.text || name.toLowerCase().includes(savedAddresses.headerItem.text)
|
||||
components: [
|
||||
StatusIcon {
|
||||
icon: "star-icon"
|
||||
width: 12
|
||||
height: 12
|
||||
}
|
||||
]
|
||||
onClicked: contactSelected(address, RecipientSelector.Type.Address )
|
||||
}
|
||||
Component {
|
||||
id: search
|
||||
StatusBaseInput {
|
||||
width: parent.width
|
||||
height: 56
|
||||
placeholderText: qsTr("Search for saved address")
|
||||
rightComponent: StatusIcon {
|
||||
icon: "search"
|
||||
height: 17
|
||||
color: Theme.palette.baseColor1
|
||||
}
|
||||
}
|
||||
}
|
||||
Component {
|
||||
id: nothingInList
|
||||
StatusBaseText {
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.directColor1
|
||||
//% "No Saved Address"
|
||||
text: qsTr("No Saved Address")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
height: myAccounts.height
|
||||
color: "transparent"
|
||||
radius: 8
|
||||
|
||||
ListView {
|
||||
id: myAccounts
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
width: parent.width
|
||||
height: Math.min(288, myAccounts.contentHeight)
|
||||
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
clip: true
|
||||
|
||||
delegate: StatusListItem {
|
||||
width: visible ? parent.width: 0
|
||||
height: visible ? 64 : 0
|
||||
title: model.name
|
||||
subTitle: Utils.toLocaleString(model.currencyBalance.toFixed(2), popup.store.locale, {"model.currency": true}) + " " + popup.store.currentCurrency.toUpperCase()
|
||||
icon.emoji: !!model.emoji ? model.emoji: ""
|
||||
icon.color: model.color
|
||||
icon.name: !model.emoji ? "filled-account": ""
|
||||
icon.letterSize: 14
|
||||
icon.isLetterIdenticon: !!model.emoji ? true : false
|
||||
icon.background.color: Theme.palette.indirectColor1
|
||||
radius: 0
|
||||
onClicked: contactSelected(model.address, RecipientSelector.Type.Account )
|
||||
}
|
||||
|
||||
model: root.store.accounts
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
height: recents.height
|
||||
color: "transparent"
|
||||
radius: 8
|
||||
|
||||
ListView {
|
||||
id: recents
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
width: parent.width
|
||||
height: Math.min(288, recents.contentHeight)
|
||||
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
clip: true
|
||||
|
||||
header: StatusBaseText {
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.directColor1
|
||||
//% "No Recents"
|
||||
text: qsTr("No Recents")
|
||||
visible: recents.count <= 0
|
||||
}
|
||||
|
||||
delegate: StatusListItem {
|
||||
property bool isIncoming: to === popup.store.currentAccount.address
|
||||
width: visible ? parent.width: 0
|
||||
height: visible ? 64 : 0
|
||||
title: isIncoming ? from : to
|
||||
subTitle: Utils.getTimeDifference(new Date(parseInt(timestamp) * 1000), new Date())
|
||||
statusListItemTitle.elide: Text.ElideMiddle
|
||||
statusListItemTitle.wrapMode: Text.NoWrap
|
||||
radius: 0
|
||||
components: [
|
||||
StatusIcon {
|
||||
id: transferIcon
|
||||
height: 15
|
||||
width: 15
|
||||
color: isIncoming ? Style.current.success : Style.current.danger
|
||||
icon: isIncoming ? "down" : "up"
|
||||
rotation: 45
|
||||
},
|
||||
StatusBaseText {
|
||||
id: contactsLabel
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.directColor1
|
||||
text: popup.store.hex2Eth(value)
|
||||
}
|
||||
]
|
||||
onClicked: contactSelected(title, RecipientSelector.Type.Address)
|
||||
}
|
||||
|
||||
model: root.store.walletSectionTransactionsInst.model
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
import QtQuick.Dialogs 1.3
|
||||
|
||||
import utils 1.0
|
||||
import shared.stores 1.0
|
||||
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Popups 0.1
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
||||
import "../panels"
|
||||
import "../controls"
|
||||
import "../views"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
height: visible ? stackLayout.height + modeSelectionTabBar.height + 2*Style.current.xlPadding: 0
|
||||
|
||||
signal contactSelected(string address, int type)
|
||||
|
||||
StatusSwitchTabBar {
|
||||
id: modeSelectionTabBar
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
StatusSwitchTabButton {
|
||||
//% "Simple"
|
||||
text: qsTr("Simple")
|
||||
}
|
||||
StatusSwitchTabButton {
|
||||
//% "Advanced"
|
||||
text: qsTr("Advanced")
|
||||
}
|
||||
StatusSwitchTabButton {
|
||||
//% "Custom"
|
||||
text: qsTr("Custom")
|
||||
}
|
||||
}
|
||||
|
||||
StackLayout {
|
||||
id: stackLayout
|
||||
anchors.top: modeSelectionTabBar.bottom
|
||||
anchors.topMargin: Style.current.xlPadding
|
||||
height: simpleLayout.height
|
||||
width: parent.width
|
||||
currentIndex: modeSelectionTabBar.currentIndex
|
||||
|
||||
ColumnLayout {
|
||||
id: simpleLayout
|
||||
Layout.fillWidth: true
|
||||
spacing: 24
|
||||
// To-do networks depends on multi networks and fee suggestions not available yet
|
||||
Rectangle {
|
||||
id: networksRect
|
||||
radius: 13
|
||||
color: Theme.palette.indirectColor1
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: layout.height + 24
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.margins: 16
|
||||
spacing: 20
|
||||
RowLayout {
|
||||
spacing: 10
|
||||
StatusRoundIcon {
|
||||
Layout.alignment: Qt.AlignTop
|
||||
radius: 8
|
||||
icon.name: "flash"
|
||||
}
|
||||
ColumnLayout {
|
||||
StatusBaseText {
|
||||
Layout.maximumWidth: 410
|
||||
font.pixelSize: 15
|
||||
font.weight: Font.Medium
|
||||
color: Theme.palette.directColor1
|
||||
//% "Networks"
|
||||
text: qsTr("Networks")
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
StatusBaseText {
|
||||
Layout.maximumWidth: 410
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.baseColor1
|
||||
//% "The networks where the receipient will receive tokens. Amounts calculated automatically for the lowest cost."
|
||||
text: qsTr("The networks where the receipient will receive tokens. Amounts calculated automatically for the lowest cost.")
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
spacing: 16
|
||||
Layout.leftMargin: 40
|
||||
Repeater {
|
||||
model: 3
|
||||
StatusListItem {
|
||||
implicitWidth: 126
|
||||
title: "PlaceHolder" + index
|
||||
subTitle: ""
|
||||
icon.isLetterIdenticon: true
|
||||
icon.width: 32
|
||||
icon.height: 32
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
color: "transparent"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: feesRect
|
||||
radius: 13
|
||||
color: Theme.palette.indirectColor1
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: feesLayout.height + 32
|
||||
RowLayout {
|
||||
id: feesLayout
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.margins: 16
|
||||
spacing: 10
|
||||
StatusRoundIcon {
|
||||
Layout.alignment: Qt.AlignTop
|
||||
radius: 8
|
||||
icon.name: "fees"
|
||||
}
|
||||
ColumnLayout {
|
||||
spacing: 12
|
||||
StatusBaseText {
|
||||
Layout.maximumWidth: 410
|
||||
font.pixelSize: 15
|
||||
font.weight: Font.Medium
|
||||
color: Theme.palette.directColor1
|
||||
//% "Fees"
|
||||
text: qsTr("Fees")
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
RowLayout {
|
||||
spacing: 16
|
||||
Repeater {
|
||||
model: 3
|
||||
delegate:
|
||||
RadioDelegate {
|
||||
id: control
|
||||
checked: index === 0
|
||||
contentItem.visible: false
|
||||
indicator.visible: false
|
||||
background: Rectangle {
|
||||
implicitWidth: 128
|
||||
implicitHeight: 78
|
||||
radius: 8
|
||||
color: control.checked ? Theme.palette.indirectColor1: Theme.palette.baseColor4
|
||||
border.width: control.checked
|
||||
border.color: Theme.palette.primaryColor2
|
||||
ColumnLayout {
|
||||
width: parent.width
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.margins: 8
|
||||
StatusBaseText {
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.baseColor1
|
||||
//% "Slow"
|
||||
text: qsTr("Slow")
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
StatusBaseText {
|
||||
font.pixelSize: 13
|
||||
color: Theme.palette.baseColor1
|
||||
text: "0.24 USD"
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
StatusBaseText {
|
||||
font.pixelSize: 13
|
||||
color: Theme.palette.baseColor1
|
||||
text: "~15 minutes"
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.baseColor1
|
||||
text: "Not Implemented"
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
font.pixelSize: 15
|
||||
color: Theme.palette.baseColor1
|
||||
text: "Not Implemented"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -620,6 +620,83 @@ QtObject {
|
|||
return JSON.parse(jsonObj)
|
||||
}
|
||||
|
||||
function getTimeDifference(d1, d2) {
|
||||
var timeString = ""
|
||||
var day1Year = d1.getFullYear()
|
||||
var day1Month = d1.getMonth()
|
||||
var day1Time = d1.getTime()
|
||||
|
||||
var day2Year = d2.getFullYear()
|
||||
var day2Month = d2.getMonth()
|
||||
var day2Time = d2.getTime()
|
||||
|
||||
var inYears = day2Year-day1Year
|
||||
|
||||
if(inYears > 0) {
|
||||
//% "years ago"
|
||||
//% "year ago"
|
||||
timeString = inYears > 1 ? qsTr("years ago") : qsTr("year ago")
|
||||
return inYears + " " + timeString
|
||||
}
|
||||
|
||||
var inMonths = (day2Month+12*day2Year)-(day1Month+12*day1Year)
|
||||
|
||||
if(inMonths > 0) {
|
||||
//% "months ago"
|
||||
//% "month ago"
|
||||
timeString = inMonths > 1 ? qsTr("months ago") : qsTr("month ago")
|
||||
return inMonths + " " + timeString
|
||||
}
|
||||
|
||||
var inWeeks = parseInt((day2Time-day2Time)/(24*3600*1000*7))
|
||||
|
||||
if(inWeeks > 0) {
|
||||
//% "weeks ago"
|
||||
//% "week ago"
|
||||
timeString = inWeeks > 1 ? qsTr("weeks ago") : qsTr("week ago")
|
||||
return inWeeks + " " + timeString
|
||||
}
|
||||
|
||||
var inDays = parseInt((day2Time-day1Time)/(24*3600*1000))
|
||||
|
||||
if(inDays > 0) {
|
||||
//% "days ago"
|
||||
//% "day ago"
|
||||
timeString = inDays > 1 ? qsTr("days ago") : qsTr("day ago")
|
||||
return inDays + " " + timeString
|
||||
}
|
||||
|
||||
var inHours = parseInt((day2Time-day1Time)/(3600*1000));
|
||||
|
||||
if(inHours > 0) {
|
||||
//% "hours ago"
|
||||
//% "hour ago"
|
||||
timeString = inHours > 1 ? qsTr("hours ago") : qsTr("hour ago")
|
||||
return inHours + " " + timeString
|
||||
}
|
||||
|
||||
var inMins = parseInt((day2Time-day1Time)/(60*1000))
|
||||
|
||||
if(inMins > 0) {
|
||||
//% "mins ago"
|
||||
//% "min ago"
|
||||
timeString = inMins > 1 ? qsTr("mins ago") : qsTr("min ago")
|
||||
return inMins + " " + timeString
|
||||
}
|
||||
|
||||
var inSecs = parseInt((day2Time-day1Time)/(1000));
|
||||
|
||||
if(inSecs > 0) {
|
||||
//% "secs ago"
|
||||
//% "sec ago"
|
||||
timeString = inSecs > 1 ? qsTr("secs ago") : qsTr("sec ago")
|
||||
return inSecs + " " + timeString
|
||||
}
|
||||
|
||||
//% "now"
|
||||
return qsTr("now")
|
||||
}
|
||||
|
||||
// Leave this function at the bottom of the file as QT Creator messes up the code color after this
|
||||
function isPunct(c) {
|
||||
return /(!|\@|#|\$|%|\^|&|\*|\(|\)|_|\+|\||-|=|\\|{|}|[|]|"|;|'|<|>|\?|,|\.|\/)/.test(c)
|
||||
|
|
Loading…
Reference in New Issue