parent
f29e825af4
commit
0903fa6ce2
|
@ -1 +1 @@
|
||||||
Subproject commit 41f67b59e954550207fb8882a5b58dd705cc4a38
|
Subproject commit b9aa49853adfb0015b7b9572d0a72ad6ebe54e7c
|
|
@ -381,6 +381,7 @@ Item {
|
||||||
id: cmpSendTransactionWithEns
|
id: cmpSendTransactionWithEns
|
||||||
SendModal {
|
SendModal {
|
||||||
id: sendTransactionWithEns
|
id: sendTransactionWithEns
|
||||||
|
anchors.centerIn: parent
|
||||||
store: root.rootStore
|
store: root.rootStore
|
||||||
contactsStore: root.contactsStore
|
contactsStore: root.contactsStore
|
||||||
onOpened: {
|
onOpened: {
|
||||||
|
@ -390,9 +391,8 @@ Item {
|
||||||
onClosed: {
|
onClosed: {
|
||||||
destroy()
|
destroy()
|
||||||
}
|
}
|
||||||
isContact: root.isContact
|
launchedFromChat: true
|
||||||
selectRecipient.readOnly: true
|
preSelectedRecipient: {
|
||||||
selectRecipient.selectedRecipient: {
|
|
||||||
parentModule.prepareChatContentModuleForChatId(activeChatId)
|
parentModule.prepareChatContentModuleForChatId(activeChatId)
|
||||||
let chatContentModule = parentModule.getChatContentModule()
|
let chatContentModule = parentModule.getChatContentModule()
|
||||||
|
|
||||||
|
@ -405,7 +405,6 @@ Item {
|
||||||
ensVerified: true
|
ensVerified: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
selectRecipient.selectedType: RecipientSelector.Type.Contact
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import QtQuick.Layouts 1.13
|
||||||
|
|
||||||
import StatusQ.Popups 0.1
|
import StatusQ.Popups 0.1
|
||||||
import StatusQ.Controls 0.1
|
import StatusQ.Controls 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
|
||||||
import utils 1.0
|
import utils 1.0
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,14 @@ QtObject {
|
||||||
|
|
||||||
property var walletSectionTransactionsInst: walletSectionTransactions
|
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) {
|
function createCommunity(communityName, communityDescription, checkedMembership, ensOnlySwitchChecked, communityColor, communityImage, imageCropperModalaX, imageCropperModalaY, imageCropperModalbX, imageCropperModalbY) {
|
||||||
communitiesModuleInst.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() {
|
function suggestedFees() {
|
||||||
return JSON.parse(walletSectionTransactions.suggestedFees())
|
return JSON.parse(walletSectionTransactions.suggestedFees())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hex2Eth(value) {
|
||||||
|
return globalUtils.hex2Eth(value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -801,6 +801,7 @@ Item {
|
||||||
}
|
}
|
||||||
property var selectedAccount
|
property var selectedAccount
|
||||||
sourceComponent: SendModal {
|
sourceComponent: SendModal {
|
||||||
|
anchors.centerIn: parent
|
||||||
store: appMain.rootStore
|
store: appMain.rootStore
|
||||||
contactsStore: appMain.rootStore.profileSectionStore.contactsStore
|
contactsStore: appMain.rootStore.profileSectionStore.contactsStore
|
||||||
onOpened: {
|
onOpened: {
|
||||||
|
@ -813,7 +814,7 @@ Item {
|
||||||
}
|
}
|
||||||
onLoaded: {
|
onLoaded: {
|
||||||
if(!!sendModal.selectedAccount) {
|
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 {
|
Input {
|
||||||
id: chatKey
|
id: chatKey
|
||||||
|
property bool hasValidSearchResult: false
|
||||||
|
|
||||||
//% "Enter ENS username or chat key"
|
//% "Enter ENS username or chat key"
|
||||||
placeholderText: qsTrId("enter-contact-code")
|
placeholderText: qsTrId("enter-contact-code")
|
||||||
visible: showSearch
|
visible: showSearch
|
||||||
|
@ -90,6 +92,7 @@ Item {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
chatKey.hasValidSearchResult = false
|
||||||
Qt.callLater(resolveENS, chatKey.text);
|
Qt.callLater(resolveENS, chatKey.text);
|
||||||
} else {
|
} else {
|
||||||
root.validationError = "";
|
root.validationError = "";
|
||||||
|
@ -100,6 +103,7 @@ Item {
|
||||||
Connections {
|
Connections {
|
||||||
target: mainModule
|
target: mainModule
|
||||||
onResolvedENS: {
|
onResolvedENS: {
|
||||||
|
chatKey.hasValidSearchResult = false
|
||||||
if (chatKey.text == "") {
|
if (chatKey.text == "") {
|
||||||
ensUsername.text = "";
|
ensUsername.text = "";
|
||||||
pubKey = "";
|
pubKey = "";
|
||||||
|
@ -113,6 +117,7 @@ Item {
|
||||||
//% "Can't chat with yourself"
|
//% "Can't chat with yourself"
|
||||||
root.validationError = qsTrId("can-t-chat-with-yourself");
|
root.validationError = qsTrId("can-t-chat-with-yourself");
|
||||||
} else {
|
} else {
|
||||||
|
chatKey.hasValidSearchResult = true
|
||||||
searchResults.username = Utils.addStatusEns(chatKey.text.trim())
|
searchResults.username = Utils.addStatusEns(chatKey.text.trim())
|
||||||
let userAlias = globalUtils.generateAlias(resolvedPubKey)
|
let userAlias = globalUtils.generateAlias(resolvedPubKey)
|
||||||
userAlias = userAlias.length > 20 ? userAlias.substring(0, 19) + "..." : userAlias
|
userAlias = userAlias.length > 20 ? userAlias.substring(0, 19) + "..." : userAlias
|
||||||
|
@ -151,6 +156,7 @@ Item {
|
||||||
noContactsRect.visible = false;
|
noContactsRect.visible = false;
|
||||||
searchResults.loading = false;
|
searchResults.loading = false;
|
||||||
root.validationError = "";
|
root.validationError = "";
|
||||||
|
chatKey.hasValidSearchResult = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,6 +205,7 @@ Item {
|
||||||
}
|
}
|
||||||
root.pubKeys = pubKeysCopy
|
root.pubKeys = pubKeysCopy
|
||||||
|
|
||||||
|
chatKey.hasValidSearchResult = false
|
||||||
userClicked(contact.pubKey, contact.isContact, contact.name, contact.address)
|
userClicked(contact.pubKey, contact.isContact, contact.name, contact.address)
|
||||||
}
|
}
|
||||||
expanded: !searchResults.loading && pubKey === "" && !searchResults.showProfileNotFoundMessage
|
expanded: !searchResults.loading && pubKey === "" && !searchResults.showProfileNotFoundMessage
|
||||||
|
@ -217,6 +224,7 @@ Item {
|
||||||
if (!validate()) {
|
if (!validate()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
chatKey.hasValidSearchResult = false
|
||||||
userClicked(pubKey, isAddedContact, username, searchResults.address)
|
userClicked(pubKey, isAddedContact, username, searchResults.address)
|
||||||
}
|
}
|
||||||
onAddToContactsButtonClicked: {
|
onAddToContactsButtonClicked: {
|
||||||
|
|
|
@ -11,8 +11,8 @@ import StatusQ.Controls 0.1
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: Style.current.smallPadding + prioritytext.height +
|
height: visible ? Style.current.smallPadding + prioritytext.height +
|
||||||
(advancedMode ? advancedModeItemGroup.height : selectorButtons.height)
|
(advancedMode ? advancedModeItemGroup.height : selectorButtons.height) : 0
|
||||||
|
|
||||||
property double gasPrice: 0
|
property double gasPrice: 0
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@ Item {
|
||||||
property alias selectedGasPrice: inputGasPrice.text
|
property alias selectedGasPrice: inputGasPrice.text
|
||||||
property alias selectedGasLimit: inputGasLimit.text
|
property alias selectedGasLimit: inputGasLimit.text
|
||||||
property string defaultGasLimit: "0"
|
property string defaultGasLimit: "0"
|
||||||
|
property string maxFiatFees: selectedGasFiatValue + root.defaultCurrency.toUpperCase()
|
||||||
|
|
||||||
|
|
||||||
property alias selectedTipLimit: inputPerGasTipLimit.text
|
property alias selectedTipLimit: inputPerGasTipLimit.text
|
||||||
|
@ -441,7 +442,7 @@ Item {
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
id: maxPriorityFeeFiatText
|
id: maxPriorityFeeFiatText
|
||||||
text: `${selectedGasFiatValue} ${root.defaultCurrency.toUpperCase()}`
|
text: root.maxFiatFees
|
||||||
anchors.verticalCenter: maxPriorityFeeText.verticalCenter
|
anchors.verticalCenter: maxPriorityFeeText.verticalCenter
|
||||||
anchors.left: maxPriorityFeeText.right
|
anchors.left: maxPriorityFeeText.right
|
||||||
anchors.leftMargin: 6
|
anchors.leftMargin: 6
|
||||||
|
|
|
@ -2,32 +2,32 @@ import QtQuick 2.13
|
||||||
import QtQuick.Controls 2.13
|
import QtQuick.Controls 2.13
|
||||||
import QtQuick.Layouts 1.13
|
import QtQuick.Layouts 1.13
|
||||||
import QtQuick.Dialogs 1.3
|
import QtQuick.Dialogs 1.3
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
import StatusQ.Controls.Validators 0.1
|
||||||
|
|
||||||
import utils 1.0
|
import utils 1.0
|
||||||
import shared.stores 1.0
|
import shared.stores 1.0
|
||||||
|
|
||||||
import StatusQ.Controls 0.1
|
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 "../panels"
|
||||||
import "../controls"
|
import "../controls"
|
||||||
import "../views"
|
import "../views"
|
||||||
import "."
|
|
||||||
|
|
||||||
// TODO: replace with StatusModal
|
StatusModal {
|
||||||
ModalPopup {
|
id: popup
|
||||||
id: root
|
|
||||||
property var contactsStore
|
|
||||||
|
|
||||||
property alias selectFromAccount: selectFromAccount
|
|
||||||
property alias selectRecipient: selectRecipient
|
|
||||||
property alias stack: stack
|
property alias stack: stack
|
||||||
|
|
||||||
property var store
|
property var store
|
||||||
property bool isContact: false
|
property var contactsStore
|
||||||
|
property var preSelectedAccount
|
||||||
//% "Send"
|
property var preSelectedRecipient
|
||||||
title: qsTrId("command-button-send")
|
property bool launchedFromChat: false
|
||||||
height: 540
|
|
||||||
|
|
||||||
property MessageDialog sendingError: MessageDialog {
|
property MessageDialog sendingError: MessageDialog {
|
||||||
id: sendingError
|
id: sendingError
|
||||||
//% "Error sending the transaction"
|
//% "Error sending the transaction"
|
||||||
|
@ -39,11 +39,11 @@ ModalPopup {
|
||||||
function sendTransaction() {
|
function sendTransaction() {
|
||||||
stack.currentGroup.isPending = true
|
stack.currentGroup.isPending = true
|
||||||
let success = false
|
let success = false
|
||||||
if(txtAmount.selectedAsset.address === "" || txtAmount.selectedAsset.address === Constants.zeroAddress){
|
if(advancedHeader.assetSelector.selectedAsset.address === "" || advancedHeader.assetSelector.selectedAsset.address === Constants.zeroAddress){
|
||||||
success = root.store.transferEth(
|
success = popup.store.transferEth(
|
||||||
selectFromAccount.selectedAccount.address,
|
advancedHeader.accountSelector.selectedAccount.address,
|
||||||
selectRecipient.selectedRecipient.address,
|
advancedHeader.recipientSelector.selectedRecipient.address,
|
||||||
txtAmount.selectedAmount,
|
advancedHeader.amountToSendInput.text,
|
||||||
gasSelector.selectedGasLimit,
|
gasSelector.selectedGasLimit,
|
||||||
gasSelector.isEIP1559Enabled ? "" : gasSelector.selectedGasPrice,
|
gasSelector.isEIP1559Enabled ? "" : gasSelector.selectedGasPrice,
|
||||||
gasSelector.selectedTipLimit,
|
gasSelector.selectedTipLimit,
|
||||||
|
@ -51,11 +51,11 @@ ModalPopup {
|
||||||
transactionSigner.enteredPassword,
|
transactionSigner.enteredPassword,
|
||||||
stack.uuid)
|
stack.uuid)
|
||||||
} else {
|
} else {
|
||||||
success = root.store.transferTokens(
|
success = popup.store.transferTokens(
|
||||||
selectFromAccount.selectedAccount.address,
|
advancedHeader.accountSelector.selectedAccount.address,
|
||||||
selectRecipient.selectedRecipient.address,
|
advancedHeader.recipientSelector.selectedRecipient.address,
|
||||||
txtAmount.selectedAsset.address,
|
advancedHeader.assetSelector.selectedAsset.address,
|
||||||
txtAmount.selectedAmount,
|
advancedHeader.amountToSendInput.text,
|
||||||
gasSelector.selectedGasLimit,
|
gasSelector.selectedGasLimit,
|
||||||
gasSelector.isEIP1559Enabled ? "" : gasSelector.selectedGasPrice,
|
gasSelector.isEIP1559Enabled ? "" : gasSelector.selectedGasPrice,
|
||||||
gasSelector.selectedTipLimit,
|
gasSelector.selectedTipLimit,
|
||||||
|
@ -63,291 +63,246 @@ ModalPopup {
|
||||||
transactionSigner.enteredPassword,
|
transactionSigner.enteredPassword,
|
||||||
stack.uuid)
|
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 {
|
width: 556
|
||||||
id: stack
|
// To-Do as per design once the account selector become floating the heigth can be as defined in design as 595
|
||||||
anchors.fill: parent
|
height: 670
|
||||||
anchors.leftMargin: Style.current.padding
|
showHeader: false
|
||||||
anchors.rightMargin: Style.current.padding
|
showFooter: false
|
||||||
onGroupActivated: {
|
showAdvancedFooter: !!popup.advancedHeader ? popup.advancedHeader.isReady && gasValidator.isValid : false
|
||||||
root.title = group.headerText
|
showAdvancedHeader: true
|
||||||
btnNext.text = group.footerText
|
|
||||||
|
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 {
|
TransactionFormGroup {
|
||||||
id: group1
|
id: group1
|
||||||
//% "Send"
|
anchors.fill: parent
|
||||||
headerText: qsTrId("command-button-send")
|
ScrollView {
|
||||||
//% "Continue"
|
height: stack.height
|
||||||
footerText: qsTrId("continue")
|
width: parent.width
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
|
||||||
StatusAccountSelector {
|
ScrollBar.vertical.policy: ScrollBar.AlwaysOff
|
||||||
id: selectFromAccount
|
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||||
accounts: root.store.accounts
|
contentHeight: addressSelector.height + networkAndFeesSelector.height + gasSelector.height + gasValidator.height
|
||||||
selectedAccount: {
|
clip: true
|
||||||
const currAcc = root.store.currentAccount
|
|
||||||
if (currAcc.walletType !== Constants.watchWalletType) {
|
TabAddressSelectorView {
|
||||||
return currAcc
|
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 {
|
TabNetworkAndFees {
|
||||||
id: txtAmount
|
id: networkAndFeesSelector
|
||||||
selectedAccount: selectFromAccount.selectedAccount
|
anchors.top: addressSelector.bottom
|
||||||
currentCurrency: root.store.currentCurrency
|
anchors.right: parent.right
|
||||||
// TODO make those use a debounce
|
anchors.left: parent.left
|
||||||
getFiatValue: root.store.getFiatValue
|
visible: popup.store.isMultiNetworkEnabled
|
||||||
// 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()
|
|
||||||
|
|
||||||
width: stack.width
|
GasSelector {
|
||||||
property var estimateGas: Backpressure.debounce(gasSelector, 600, function() {
|
id: gasSelector
|
||||||
if (!(selectFromAccount.selectedAccount && selectFromAccount.selectedAccount.address &&
|
anchors.top: networkAndFeesSelector.visible ? networkAndFeesSelector.bottom : addressSelector.bottom
|
||||||
selectRecipient.selectedRecipient && selectRecipient.selectedRecipient.address &&
|
gasPrice: parseFloat(popup.store.gasPrice)
|
||||||
txtAmount.selectedAsset && txtAmount.selectedAsset.address &&
|
getGasEthValue: popup.store.getGasEthValue
|
||||||
txtAmount.selectedAmount)) {
|
getFiatValue: popup.store.getFiatValue
|
||||||
selectedGasLimit = 250000
|
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
|
defaultGasLimit = selectedGasLimit
|
||||||
return
|
})
|
||||||
}
|
}
|
||||||
|
GasValidator {
|
||||||
let gasEstimate = JSON.parse(root.store.estimateGas(
|
id: gasValidator
|
||||||
selectFromAccount.selectedAccount.address,
|
anchors.top: gasSelector.bottom
|
||||||
selectRecipient.selectedRecipient.address,
|
selectedAccount: advancedHeader.accountSelector.selectedAccount
|
||||||
txtAmount.selectedAsset.address,
|
selectedAmount: parseFloat(advancedHeader.amountToSendInput.text)
|
||||||
txtAmount.selectedAmount,
|
selectedAsset: advancedHeader.assetSelector.selectedAsset
|
||||||
""))
|
selectedGasEthValue: gasSelector.selectedGasEthValue
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
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 {
|
TransactionFormGroup {
|
||||||
id: group4
|
id: group4
|
||||||
//% "Sign with password"
|
|
||||||
headerText: qsTrId("sign-with-password")
|
StackView.onActivated: {
|
||||||
//% "Send %1 %2"
|
transactionSigner.forceActiveFocus(Qt.MouseFocusReason)
|
||||||
footerText: qsTrId("send--1--2").arg(txtAmount.selectedAmount).arg(!!txtAmount.selectedAsset ? txtAmount.selectedAsset.symbol : "")
|
}
|
||||||
|
|
||||||
TransactionSigner {
|
TransactionSigner {
|
||||||
id: transactionSigner
|
id: transactionSigner
|
||||||
|
Layout.topMargin: Style.current.smallPadding
|
||||||
width: stack.width
|
width: stack.width
|
||||||
signingPhrase: root.store.signingPhrase
|
signingPhrase: popup.store.signingPhrase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
footer: Item {
|
advancedFooterComponent: SendModalFooter {
|
||||||
width: parent.width
|
maxFiatFees: gasSelector.maxFiatFees
|
||||||
height: btnNext.height
|
currentGroupPending: popup.contentItem.currentGroup.isPending
|
||||||
|
currentGroupValid: popup.contentItem.currentGroup.isValid
|
||||||
StatusRoundButton {
|
isLastGroup: popup.contentItem.isLastGroup
|
||||||
id: btnBack
|
onNextButtonClicked: {
|
||||||
anchors.left: parent.left
|
const validity = popup.contentItem.currentGroup.validate()
|
||||||
visible: !stack.isFirstGroup
|
if (validity.isValid && !validity.isPending) {
|
||||||
icon.name: "arrow-right"
|
if (popup.contentItem.isLastGroup) {
|
||||||
icon.width: 20
|
return popup.sendTransaction()
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
// Not Refactored Yet
|
||||||
Connections {
|
// onTransactionCompleted: {
|
||||||
target: root.store.walletSectionTransactionsInst
|
// if (success) {
|
||||||
onTransactionSent: {
|
// //% "Transaction completed"
|
||||||
try {
|
// Global.toastMessage.title = qsTrId("transaction-completed")
|
||||||
let response = JSON.parse(txResult)
|
// Global.toastMessage.source = Style.svg("check-circle")
|
||||||
if (response.uuid !== stack.uuid) return
|
// Global.toastMessage.iconColor = Style.current.success
|
||||||
|
// } else {
|
||||||
stack.currentGroup.isPending = false
|
// //% "Transaction failed"
|
||||||
|
// Global.toastMessage.title = qsTrId("ens-registration-failed-title")
|
||||||
if (!response.success) {
|
// Global.toastMessage.source = Style.svg("block-icon")
|
||||||
if (Utils.isInvalidPasswordMessage(response.result)){
|
// Global.toastMessage.iconColor = Style.current.danger
|
||||||
//% "Wrong password"
|
// }
|
||||||
transactionSigner.validationError = qsTrId("wrong-password")
|
// Global.toastMessage.link = `${walletModel.utilsView.etherscanLink}/${txHash}`
|
||||||
return
|
// Global.toastMessage.open()
|
||||||
}
|
// }
|
||||||
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()
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*##^##
|
|
||||||
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)
|
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
|
// Leave this function at the bottom of the file as QT Creator messes up the code color after this
|
||||||
function isPunct(c) {
|
function isPunct(c) {
|
||||||
return /(!|\@|#|\$|%|\^|&|\*|\(|\)|_|\+|\||-|=|\\|{|}|[|]|"|;|'|<|>|\?|,|\.|\/)/.test(c)
|
return /(!|\@|#|\$|%|\^|&|\*|\(|\)|_|\+|\||-|=|\\|{|}|[|]|"|;|'|<|>|\?|,|\.|\/)/.test(c)
|
||||||
|
|
Loading…
Reference in New Issue