mirror of
https://github.com/logos-blockchain/logos-blockchain-ui.git
synced 2026-07-08 00:39:28 +00:00
Modify views
This commit is contained in:
parent
422166b2b4
commit
c34ba1552f
@ -170,19 +170,32 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
// Page 2: Node control, wallet, logs + Channel Deposit (tabbed)
|
||||
// Page 2: Node information + Wallet operations (tabbed)
|
||||
ColumnLayout {
|
||||
id: opPage
|
||||
spacing: Theme.spacing.medium
|
||||
|
||||
// Selected operation inside the Operations tab's sidebar nav.
|
||||
// 0 Accounts · 1 Transfer · 2 Leader Rewards · 3 Channel Deposit
|
||||
property int operationIndex: 0
|
||||
|
||||
readonly property bool nodeRunning: root.backend
|
||||
? root.backend.status === BlockchainBackend.Running
|
||||
: false
|
||||
|
||||
// Channel Deposit requires a running node. If the node stops while
|
||||
// it's selected, fall back to Accounts so the user isn't stranded
|
||||
// on a disabled nav item.
|
||||
onNodeRunningChanged: {
|
||||
if (!nodeRunning && operationIndex === 3)
|
||||
operationIndex = 0
|
||||
}
|
||||
|
||||
LogosTabBar {
|
||||
id: operationTabBar
|
||||
Layout.fillWidth: true
|
||||
LogosTabButton { text: qsTr("Node & Wallet") }
|
||||
LogosTabButton {
|
||||
text: qsTr("Channel Deposit")
|
||||
enabled: root.backend
|
||||
&& root.backend.status === BlockchainBackend.Running
|
||||
}
|
||||
LogosTabButton { text: qsTr("Node") }
|
||||
LogosTabButton { text: qsTr("Operations") }
|
||||
}
|
||||
|
||||
StackLayout {
|
||||
@ -191,150 +204,240 @@ Rectangle {
|
||||
Layout.fillHeight: true
|
||||
currentIndex: operationTabBar.currentIndex
|
||||
|
||||
SplitView {
|
||||
orientation: Qt.Vertical
|
||||
// ---- Tab 0: Node information (status + logs) ----
|
||||
SplitView {
|
||||
orientation: Qt.Vertical
|
||||
|
||||
ColumnLayout {
|
||||
SplitView.fillWidth: true
|
||||
SplitView.minimumHeight: 200
|
||||
spacing: Theme.spacing.large
|
||||
ColumnLayout {
|
||||
SplitView.fillWidth: true
|
||||
SplitView.minimumHeight: 120
|
||||
spacing: Theme.spacing.large
|
||||
|
||||
StatusConfigView {
|
||||
Layout.fillWidth: true
|
||||
statusText: root.backend
|
||||
? _d.getStatusString(root.backend.status)
|
||||
: qsTr("Not Connected")
|
||||
statusColor: root.backend
|
||||
? _d.getStatusColor(root.backend.status)
|
||||
: Theme.palette.error
|
||||
userConfig: root.backend ? root.backend.userConfig : ""
|
||||
deploymentConfig: root.backend ? root.backend.deploymentConfig : ""
|
||||
useGeneratedConfig: root.backend ? root.backend.useGeneratedConfig : false
|
||||
canStart: root.backend
|
||||
&& !!root.backend.userConfig
|
||||
&& root.backend.status !== BlockchainBackend.Starting
|
||||
&& root.backend.status !== BlockchainBackend.Stopping
|
||||
isRunning: root.backend
|
||||
? root.backend.status === BlockchainBackend.Running
|
||||
: false
|
||||
StatusConfigView {
|
||||
Layout.fillWidth: true
|
||||
statusText: root.backend
|
||||
? _d.getStatusString(root.backend.status)
|
||||
: qsTr("Not Connected")
|
||||
statusColor: root.backend
|
||||
? _d.getStatusColor(root.backend.status)
|
||||
: Theme.palette.error
|
||||
userConfig: root.backend ? root.backend.userConfig : ""
|
||||
deploymentConfig: root.backend ? root.backend.deploymentConfig : ""
|
||||
useGeneratedConfig: root.backend ? root.backend.useGeneratedConfig : false
|
||||
canStart: root.backend
|
||||
&& !!root.backend.userConfig
|
||||
&& root.backend.status !== BlockchainBackend.Starting
|
||||
&& root.backend.status !== BlockchainBackend.Stopping
|
||||
isRunning: opPage.nodeRunning
|
||||
|
||||
onStartRequested: if (root.backend) root.backend.startBlockchain()
|
||||
onStopRequested: if (root.backend) root.backend.stopBlockchain()
|
||||
onChangeConfigRequested: _d.currentPage = 0
|
||||
onStartRequested: if (root.backend) root.backend.startBlockchain()
|
||||
onStopRequested: if (root.backend) root.backend.stopBlockchain()
|
||||
onChangeConfigRequested: _d.currentPage = 0
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredHeight: Theme.spacing.small
|
||||
}
|
||||
}
|
||||
|
||||
LogsView {
|
||||
SplitView.fillWidth: true
|
||||
SplitView.minimumHeight: 150
|
||||
|
||||
logModel: root.logModel
|
||||
onClearRequested: if (root.backend) root.backend.clearLogs()
|
||||
onCopyToClipboard: (text) => {
|
||||
root.copyText(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WalletView {
|
||||
id: walletView
|
||||
accountsModel: root.accountsModel
|
||||
// ---- Tab 1: Wallet operations (sidebar nav + panels) ----
|
||||
RowLayout {
|
||||
spacing: Theme.spacing.large
|
||||
|
||||
onGetBalanceRequested: function(addressHex) {
|
||||
if (!root.backend) return
|
||||
logos.watch(
|
||||
root.backend.getBalance(addressHex),
|
||||
function(result) {
|
||||
if (result.success) {
|
||||
walletView.lastBalanceErrorAddress = ""
|
||||
walletView.lastBalanceError = ""
|
||||
} else {
|
||||
walletView.lastBalanceErrorAddress = addressHex
|
||||
walletView.lastBalanceError = _d.errorText(result.error)
|
||||
}
|
||||
},
|
||||
function(error) {
|
||||
walletView.lastBalanceErrorAddress = addressHex
|
||||
walletView.lastBalanceError = _d.errorText(error)
|
||||
// Sidebar navigation
|
||||
ColumnLayout {
|
||||
Layout.preferredWidth: 180
|
||||
Layout.fillHeight: true
|
||||
Layout.alignment: Qt.AlignTop
|
||||
spacing: Theme.spacing.small
|
||||
|
||||
NavItem { label: qsTr("Accounts"); index: 0 }
|
||||
NavItem { label: qsTr("Transfer"); index: 1 }
|
||||
NavItem { label: qsTr("Leader Rewards"); index: 2 }
|
||||
NavItem {
|
||||
label: qsTr("Channel Deposit")
|
||||
index: 3
|
||||
itemEnabled: opPage.nodeRunning
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.preferredWidth: 1
|
||||
Layout.fillHeight: true
|
||||
color: Theme.palette.borderSecondary
|
||||
}
|
||||
|
||||
// Operation panels
|
||||
StackLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
currentIndex: opPage.operationIndex
|
||||
|
||||
AccountsView {
|
||||
id: accountsView
|
||||
accountsModel: root.accountsModel
|
||||
|
||||
onGetBalanceRequested: function(addressHex) {
|
||||
if (!root.backend) return
|
||||
logos.watch(
|
||||
root.backend.getBalance(addressHex),
|
||||
function(result) {
|
||||
if (result.success) {
|
||||
accountsView.lastBalanceErrorAddress = ""
|
||||
accountsView.lastBalanceError = ""
|
||||
} else {
|
||||
accountsView.lastBalanceErrorAddress = addressHex
|
||||
accountsView.lastBalanceError = _d.errorText(result.error)
|
||||
}
|
||||
},
|
||||
function(error) {
|
||||
accountsView.lastBalanceErrorAddress = addressHex
|
||||
accountsView.lastBalanceError = _d.errorText(error)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
onCopyToClipboard: (text) => {
|
||||
root.copyText(text)
|
||||
}
|
||||
onTransferRequested: function(fromKeyHex, toKeyHex, amount) {
|
||||
if (!root.backend) return
|
||||
logos.watch(
|
||||
root.backend.transferFunds(fromKeyHex, toKeyHex, amount),
|
||||
function(result) {
|
||||
if (result.success) {
|
||||
walletView.setTransferResult(result.value)
|
||||
} else {
|
||||
walletView.setTransferResult(_d.errorText(result.error))
|
||||
}
|
||||
},
|
||||
function(error) { walletView.setTransferResult(_d.errorText(error)) }
|
||||
)
|
||||
}
|
||||
onClaimLeaderRewardsRequested: function() {
|
||||
if (!root.backend) return
|
||||
logos.watch(
|
||||
root.backend.claimLeaderRewards(),
|
||||
function(result) {
|
||||
if (result.success) {
|
||||
walletView.setLeaderClaimResult(result.value)
|
||||
} else {
|
||||
walletView.setLeaderClaimResult(_d.errorText(result.error))
|
||||
}
|
||||
},
|
||||
function(error) { walletView.setLeaderClaimResult(_d.errorText(error)) }
|
||||
)
|
||||
}
|
||||
onRefreshAccountsRequested: if (root.backend) root.backend.refreshAccounts()
|
||||
}
|
||||
onRefreshAccountsRequested: if (root.backend) root.backend.refreshAccounts()
|
||||
onCopyToClipboard: (text) => {
|
||||
root.copyText(text)
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredHeight: Theme.spacing.small
|
||||
TransferView {
|
||||
id: transferView
|
||||
accountsModel: root.accountsModel
|
||||
|
||||
onTransferRequested: function(fromKeyHex, toKeyHex, amount) {
|
||||
if (!root.backend) return
|
||||
logos.watch(
|
||||
root.backend.transferFunds(fromKeyHex, toKeyHex, amount),
|
||||
function(result) {
|
||||
if (result.success) {
|
||||
transferView.setTransferResult(result.value)
|
||||
} else {
|
||||
transferView.setTransferResult(_d.errorText(result.error))
|
||||
}
|
||||
},
|
||||
function(error) { transferView.setTransferResult(_d.errorText(error)) }
|
||||
)
|
||||
}
|
||||
onCopyToClipboard: (text) => {
|
||||
root.copyText(text)
|
||||
}
|
||||
}
|
||||
|
||||
LeaderRewardsView {
|
||||
id: leaderRewardsView
|
||||
|
||||
onClaimLeaderRewardsRequested: function() {
|
||||
if (!root.backend) return
|
||||
logos.watch(
|
||||
root.backend.claimLeaderRewards(),
|
||||
function(result) {
|
||||
if (result.success) {
|
||||
leaderRewardsView.setLeaderClaimResult(result.value)
|
||||
} else {
|
||||
leaderRewardsView.setLeaderClaimResult(_d.errorText(result.error))
|
||||
}
|
||||
},
|
||||
function(error) { leaderRewardsView.setLeaderClaimResult(_d.errorText(error)) }
|
||||
)
|
||||
}
|
||||
onCopyToClipboard: (text) => {
|
||||
root.copyText(text)
|
||||
}
|
||||
}
|
||||
|
||||
ChannelDepositView {
|
||||
id: channelDepositView
|
||||
accountsModel: root.accountsModel
|
||||
nodeRunning: opPage.nodeRunning
|
||||
|
||||
onGetNotesRequested: function(addressHex, optionalTipHex) {
|
||||
if (!root.backend) return
|
||||
logos.watch(
|
||||
root.backend.getNotes(addressHex, optionalTipHex),
|
||||
function(result) {
|
||||
if (result.success)
|
||||
channelDepositView.setNotes(result.value)
|
||||
else
|
||||
channelDepositView.setNotesError(_d.errorText(result.error))
|
||||
},
|
||||
function(error) { channelDepositView.setNotesError(_d.errorText(error)) }
|
||||
)
|
||||
}
|
||||
onSubmitRequested: function(channelIdHex, inputNoteIdHexes, metadataBase58, changePublicKeyHex, fundingPublicKeyHexes, maxTxFee, optionalTipHex) {
|
||||
if (!root.backend) return
|
||||
logos.watch(
|
||||
root.backend.channelDepositWithNotes(
|
||||
channelIdHex, inputNoteIdHexes, metadataBase58,
|
||||
changePublicKeyHex, fundingPublicKeyHexes, maxTxFee, optionalTipHex),
|
||||
function(result) {
|
||||
if (result.success)
|
||||
channelDepositView.setSubmitResult(true, result.value)
|
||||
else
|
||||
channelDepositView.setSubmitResult(false, _d.errorText(result.error))
|
||||
},
|
||||
function(error) { channelDepositView.setSubmitResult(false, _d.errorText(error)) }
|
||||
)
|
||||
}
|
||||
onCopyToClipboard: (text) => {
|
||||
root.copyText(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LogsView {
|
||||
SplitView.fillWidth: true
|
||||
SplitView.minimumHeight: 150
|
||||
// Sidebar nav entry used by the Operations tab.
|
||||
component NavItem: Rectangle {
|
||||
property string label
|
||||
property int index
|
||||
property bool itemEnabled: true
|
||||
|
||||
logModel: root.logModel
|
||||
onClearRequested: if (root.backend) root.backend.clearLogs()
|
||||
onCopyToClipboard: (text) => {
|
||||
root.copyText(text)
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 40
|
||||
radius: Theme.spacing.radiusSmall
|
||||
color: opPage.operationIndex === index
|
||||
? Theme.palette.backgroundTertiary
|
||||
: (navMouse.containsMouse ? Theme.palette.backgroundSecondary : "transparent")
|
||||
opacity: itemEnabled ? 1.0 : 0.4
|
||||
|
||||
LogosText {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: Theme.spacing.medium
|
||||
anchors.rightMargin: Theme.spacing.medium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: label
|
||||
elide: Text.ElideRight
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.bold: opPage.operationIndex === index
|
||||
color: opPage.operationIndex === index
|
||||
? Theme.palette.primary
|
||||
: Theme.palette.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ChannelDepositView {
|
||||
id: channelDepositView
|
||||
accountsModel: root.accountsModel
|
||||
nodeRunning: root.backend
|
||||
? root.backend.status === BlockchainBackend.Running
|
||||
: false
|
||||
|
||||
onGetNotesRequested: function(addressHex, optionalTipHex) {
|
||||
if (!root.backend) return
|
||||
logos.watch(
|
||||
root.backend.getNotes(addressHex, optionalTipHex),
|
||||
function(result) {
|
||||
if (result.success)
|
||||
channelDepositView.setNotes(result.value)
|
||||
else
|
||||
channelDepositView.setNotesError(_d.errorText(result.error))
|
||||
},
|
||||
function(error) { channelDepositView.setNotesError(_d.errorText(error)) }
|
||||
)
|
||||
}
|
||||
onSubmitRequested: function(channelIdHex, inputNoteIdHexes, metadataBase58, changePublicKeyHex, fundingPublicKeyHexes, maxTxFee, optionalTipHex) {
|
||||
if (!root.backend) return
|
||||
logos.watch(
|
||||
root.backend.channelDepositWithNotes(
|
||||
channelIdHex, inputNoteIdHexes, metadataBase58,
|
||||
changePublicKeyHex, fundingPublicKeyHexes, maxTxFee, optionalTipHex),
|
||||
function(result) {
|
||||
if (result.success)
|
||||
channelDepositView.setSubmitResult(true, result.value)
|
||||
else
|
||||
channelDepositView.setSubmitResult(false, _d.errorText(result.error))
|
||||
},
|
||||
function(error) { channelDepositView.setSubmitResult(false, _d.errorText(error)) }
|
||||
)
|
||||
}
|
||||
onCopyToClipboard: (text) => {
|
||||
root.copyText(text)
|
||||
}
|
||||
MouseArea {
|
||||
id: navMouse
|
||||
anchors.fill: parent
|
||||
enabled: itemEnabled
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: opPage.operationIndex = index
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
79
src/qml/views/AccountsView.qml
Normal file
79
src/qml/views/AccountsView.qml
Normal file
@ -0,0 +1,79 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Logos.Theme
|
||||
import Logos.Controls
|
||||
|
||||
import "../controls"
|
||||
|
||||
// Accounts panel: the list of known wallet addresses with per-account
|
||||
// balance refresh and copy. Extracted from the former WalletView.
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
required property var accountsModel
|
||||
|
||||
property string lastBalanceError: ""
|
||||
property string lastBalanceErrorAddress: ""
|
||||
|
||||
signal getBalanceRequested(string addressHex)
|
||||
signal refreshAccountsRequested()
|
||||
signal copyToClipboard(string text)
|
||||
|
||||
spacing: Theme.spacing.large
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
color: Theme.palette.backgroundTertiary
|
||||
radius: Theme.spacing.radiusLarge
|
||||
border.color: Theme.palette.border
|
||||
border.width: 1
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacing.large
|
||||
spacing: Theme.spacing.large
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
LogosText {
|
||||
text: qsTr("Accounts")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.bold: true
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
LogosButton {
|
||||
text: qsTr("Refresh")
|
||||
padding: Theme.spacing.small
|
||||
onClicked: root.refreshAccountsRequested()
|
||||
}
|
||||
}
|
||||
|
||||
LogosText {
|
||||
text: qsTr("Start node to see accounts here.")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.textSecondary
|
||||
wrapMode: Text.WordWrap
|
||||
visible: balanceListView.count === 0
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: balanceListView
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
clip: true
|
||||
model: root.accountsModel
|
||||
spacing: Theme.spacing.small
|
||||
|
||||
delegate: AccountDelegate {
|
||||
balanceError: root.lastBalanceErrorAddress === model.address ?
|
||||
root.lastBalanceError : ""
|
||||
onGetBalanceRequested: (addr) => root.getBalanceRequested(addr)
|
||||
onCopyRequested: (text) => root.copyToClipboard(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
87
src/qml/views/LeaderRewardsView.qml
Normal file
87
src/qml/views/LeaderRewardsView.qml
Normal file
@ -0,0 +1,87 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Logos.Theme
|
||||
import Logos.Controls
|
||||
|
||||
import "../controls"
|
||||
|
||||
// Leader rewards panel. Extracted from the former WalletView.
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
signal claimLeaderRewardsRequested()
|
||||
signal copyToClipboard(string text)
|
||||
|
||||
function setLeaderClaimResult(text) {
|
||||
leaderClaimResultText.text = text
|
||||
}
|
||||
|
||||
spacing: Theme.spacing.large
|
||||
|
||||
Rectangle {
|
||||
id: leaderRewardsRect
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: leaderRewardsCol.height + 2 * Theme.spacing.large
|
||||
color: Theme.palette.backgroundTertiary
|
||||
radius: Theme.spacing.radiusLarge
|
||||
border.color: Theme.palette.border
|
||||
border.width: 1
|
||||
|
||||
ColumnLayout {
|
||||
id: leaderRewardsCol
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Theme.spacing.large
|
||||
spacing: Theme.spacing.small
|
||||
|
||||
LogosText {
|
||||
text: qsTr("Leader rewards")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
LogosButton {
|
||||
id: leaderClaimButton
|
||||
Layout.preferredWidth: 140
|
||||
text: qsTr("Claim")
|
||||
onClicked: root.claimLeaderRewardsRequested()
|
||||
}
|
||||
|
||||
LogosButton {
|
||||
Layout.fillWidth: true
|
||||
enabled: true
|
||||
padding: Theme.spacing.small
|
||||
contentItem: RowLayout {
|
||||
width: parent.width
|
||||
anchors.centerIn: parent
|
||||
LogosText {
|
||||
id: leaderClaimResultText
|
||||
Layout.fillWidth: true
|
||||
color: Theme.palette.textSecondary
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.weight: Theme.typography.weightMedium
|
||||
wrapMode: Text.WordWrap
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
LogosCopyButton {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.preferredHeight: 40
|
||||
Layout.preferredWidth: 40
|
||||
onCopyText: root.copyToClipboard(leaderClaimResultText.text)
|
||||
visible: leaderClaimResultText.text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
}
|
||||
207
src/qml/views/TransferView.qml
Normal file
207
src/qml/views/TransferView.qml
Normal file
@ -0,0 +1,207 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Logos.Theme
|
||||
import Logos.Controls
|
||||
|
||||
import "../controls"
|
||||
|
||||
// Transfer funds panel. Extracted from the former WalletView.
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
required property var accountsModel
|
||||
|
||||
signal transferRequested(string fromKeyHex, string toKeyHex, string amount)
|
||||
signal copyToClipboard(string text)
|
||||
|
||||
function setTransferResult(text) {
|
||||
transferResultText.text = text
|
||||
}
|
||||
|
||||
spacing: Theme.spacing.large
|
||||
|
||||
Rectangle {
|
||||
id: transferRect
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: transferCol.height + 2 * Theme.spacing.large
|
||||
color: Theme.palette.backgroundTertiary
|
||||
radius: Theme.spacing.radiusLarge
|
||||
border.color: Theme.palette.border
|
||||
border.width: 1
|
||||
|
||||
ColumnLayout {
|
||||
id: transferCol
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Theme.spacing.large
|
||||
spacing: Theme.spacing.small
|
||||
|
||||
LogosText {
|
||||
text: qsTr("Transfer funds")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
StyledAddressComboBox {
|
||||
id: transferFromCombo
|
||||
model: root.accountsModel
|
||||
textRole: "address"
|
||||
}
|
||||
|
||||
LogosTextField {
|
||||
id: transferToField
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 30
|
||||
placeholderText: qsTr("To key (64 hex chars)")
|
||||
}
|
||||
|
||||
LogosTextField {
|
||||
id: transferAmountField
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 30
|
||||
placeholderText: qsTr("Amount")
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: transferButton.implicitHeight
|
||||
|
||||
LogosButton {
|
||||
id: transferButton
|
||||
Layout.preferredWidth: 60
|
||||
Layout.alignment: Qt.AlignRight
|
||||
text: qsTr("Send")
|
||||
onClicked: root.transferRequested(transferFromCombo.currentText.trim(), transferToField.text.trim(), transferAmountField.text)
|
||||
}
|
||||
|
||||
LogosButton {
|
||||
Layout.fillWidth: true
|
||||
enabled: true
|
||||
padding: Theme.spacing.small
|
||||
contentItem: RowLayout {
|
||||
width: parent.width
|
||||
anchors.centerIn: parent
|
||||
LogosText {
|
||||
id: transferResultText
|
||||
Layout.fillWidth: true
|
||||
color: Theme.palette.textSecondary
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.weight: Theme.typography.weightMedium
|
||||
wrapMode: Text.WordWrap
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
LogosCopyButton {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.preferredHeight: 40
|
||||
Layout.preferredWidth: 40
|
||||
onCopyText: root.copyToClipboard(transferResultText.text)
|
||||
visible: transferResultText.text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
|
||||
component StyledAddressComboBox: ComboBox {
|
||||
id: comboControl
|
||||
|
||||
Layout.fillWidth: true
|
||||
padding: Theme.spacing.large
|
||||
editable: true
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
|
||||
background: Rectangle {
|
||||
color: Theme.palette.backgroundTertiary
|
||||
radius: Theme.spacing.radiusLarge
|
||||
border.color: Theme.palette.border
|
||||
border.width: 1
|
||||
}
|
||||
indicator: LogosText {
|
||||
id: comboIndicator
|
||||
text: "▼"
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.textSecondary
|
||||
x: comboControl.width - width - Theme.spacing.small
|
||||
y: (comboControl.height - height) / 2
|
||||
visible: comboControl.count > 0
|
||||
}
|
||||
contentItem: Item {
|
||||
implicitWidth: 200
|
||||
implicitHeight: 30
|
||||
|
||||
TextField {
|
||||
id: comboTextField
|
||||
anchors.fill: parent
|
||||
leftPadding: 0
|
||||
rightPadding: comboControl.count > 0 ? comboIndicator.width + Theme.spacing.small : Theme.spacing.small
|
||||
topPadding: 0
|
||||
bottomPadding: 0
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
text: comboControl.editText
|
||||
onTextChanged: if (text !== comboControl.editText) comboControl.editText = text
|
||||
selectByMouse: true
|
||||
color: Theme.palette.text
|
||||
background: Item { }
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
visible: comboControl.count > 0
|
||||
z: 1
|
||||
onPressed: {
|
||||
comboControl.popup.visible ? comboControl.popup.close() : comboControl.popup.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
delegate: ItemDelegate {
|
||||
id: comboDelegate
|
||||
width: comboControl.width
|
||||
contentItem: LogosText {
|
||||
width: parent.width
|
||||
height: contentHeight + Theme.spacing.large
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.bold: true
|
||||
text: (typeof model.address !== "undefined" ? model.address : modelData) || ""
|
||||
elide: Text.ElideMiddle
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
background: Rectangle {
|
||||
color: comboDelegate.highlighted ?
|
||||
Theme.palette.backgroundTertiary :
|
||||
Theme.palette.backgroundSecondary
|
||||
}
|
||||
highlighted: comboControl.highlightedIndex === index
|
||||
}
|
||||
popup: Popup {
|
||||
y: comboControl.height - 1
|
||||
width: comboControl.width
|
||||
height: contentItem.implicitHeight
|
||||
padding: 1
|
||||
|
||||
onOpened: if (comboControl.count === 0) close()
|
||||
|
||||
contentItem: ListView {
|
||||
clip: true
|
||||
implicitHeight: contentHeight
|
||||
model: comboControl.popup.visible ? comboControl.delegateModel : null
|
||||
ScrollIndicator.vertical: ScrollIndicator { }
|
||||
highlightFollowsCurrentItem: false
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: Theme.palette.backgroundSecondary
|
||||
border.color: Theme.palette.border
|
||||
border.width: 1
|
||||
radius: Theme.spacing.radiusLarge
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,345 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import Logos.Theme
|
||||
import Logos.Controls
|
||||
|
||||
import "../controls"
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
|
||||
required property var accountsModel
|
||||
|
||||
property string lastBalanceError: ""
|
||||
property string lastBalanceErrorAddress: ""
|
||||
|
||||
signal getBalanceRequested(string addressHex)
|
||||
signal refreshAccountsRequested()
|
||||
signal transferRequested(string fromKeyHex, string toKeyHex, string amount)
|
||||
signal claimLeaderRewardsRequested()
|
||||
signal copyToClipboard(string text)
|
||||
|
||||
function setTransferResult(text) {
|
||||
transferResultText.text = text
|
||||
}
|
||||
|
||||
function setLeaderClaimResult(text) {
|
||||
leaderClaimResultText.text = text
|
||||
}
|
||||
|
||||
spacing: Theme.spacing.medium
|
||||
|
||||
// Get balance card
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: actionsCol.height
|
||||
Layout.preferredHeight: Math.min(implicitHeight, 400)
|
||||
color: Theme.palette.backgroundTertiary
|
||||
radius: Theme.spacing.radiusLarge
|
||||
border.color: Theme.palette.border
|
||||
border.width: 1
|
||||
|
||||
ColumnLayout {
|
||||
id: balanceCol
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.margins: Theme.spacing.large
|
||||
spacing: Theme.spacing.large
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
LogosText {
|
||||
text: qsTr("Accounts")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.bold: true
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
LogosButton {
|
||||
text: qsTr("Refresh")
|
||||
padding: Theme.spacing.small
|
||||
onClicked: root.refreshAccountsRequested()
|
||||
}
|
||||
}
|
||||
|
||||
LogosText {
|
||||
text: qsTr("Start node to see accounts here.")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.textSecondary
|
||||
wrapMode: Text.WordWrap
|
||||
visible: balanceListView.count === 0
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: balanceListView
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Math.min(contentHeight, 320)
|
||||
clip: true
|
||||
model: root.accountsModel
|
||||
spacing: Theme.spacing.small
|
||||
|
||||
delegate: AccountDelegate {
|
||||
balanceError: root.lastBalanceErrorAddress === model.address ?
|
||||
root.lastBalanceError: ""
|
||||
onGetBalanceRequested: (addr) => root.getBalanceRequested(addr)
|
||||
onCopyRequested: (text) => root.copyToClipboard(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: actionsCol
|
||||
|
||||
Layout.fillWidth: true
|
||||
spacing: Theme.spacing.large
|
||||
|
||||
// Transfer funds card
|
||||
Rectangle {
|
||||
id: transferRect
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: transferCol.height + 2 * Theme.spacing.large
|
||||
color: Theme.palette.backgroundTertiary
|
||||
radius: Theme.spacing.radiusLarge
|
||||
border.color: Theme.palette.border
|
||||
border.width: 1
|
||||
|
||||
ColumnLayout {
|
||||
id: transferCol
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Theme.spacing.large
|
||||
spacing: Theme.spacing.small
|
||||
|
||||
LogosText {
|
||||
text: qsTr("Transfer funds")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
StyledAddressComboBox {
|
||||
id: transferFromCombo
|
||||
model: root.accountsModel
|
||||
textRole: "address"
|
||||
}
|
||||
|
||||
LogosTextField {
|
||||
id: transferToField
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 30
|
||||
placeholderText: qsTr("To key (64 hex chars)")
|
||||
}
|
||||
|
||||
LogosTextField {
|
||||
id: transferAmountField
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 30
|
||||
placeholderText: qsTr("Amount")
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: transferButton.implicitHeight
|
||||
|
||||
LogosButton {
|
||||
id: transferButton
|
||||
Layout.preferredWidth: 60
|
||||
Layout.alignment: Qt.AlignRight
|
||||
text: qsTr("Send")
|
||||
onClicked: root.transferRequested(transferFromCombo.currentText.trim(), transferToField.text.trim(), transferAmountField.text)
|
||||
}
|
||||
|
||||
LogosButton {
|
||||
Layout.fillWidth: true
|
||||
enabled: true
|
||||
padding: Theme.spacing.small
|
||||
contentItem: RowLayout {
|
||||
width: parent.width
|
||||
anchors.centerIn: parent
|
||||
LogosText {
|
||||
id: transferResultText
|
||||
Layout.fillWidth: true
|
||||
color: Theme.palette.textSecondary
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.weight: Theme.typography.weightMedium
|
||||
wrapMode: Text.WordWrap
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
LogosCopyButton {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.preferredHeight: 40
|
||||
Layout.preferredWidth: 40
|
||||
onCopyText: root.copyToClipboard(transferResultText.text)
|
||||
visible: transferResultText.text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Leader rewards card
|
||||
Rectangle {
|
||||
id: leaderRewardsRect
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: leaderRewardsCol.height + 2 * Theme.spacing.large
|
||||
color: Theme.palette.backgroundTertiary
|
||||
radius: Theme.spacing.radiusLarge
|
||||
border.color: Theme.palette.border
|
||||
border.width: 1
|
||||
|
||||
ColumnLayout {
|
||||
id: leaderRewardsCol
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Theme.spacing.large
|
||||
spacing: Theme.spacing.small
|
||||
|
||||
LogosText {
|
||||
text: qsTr("Leader rewards")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
LogosButton {
|
||||
id: leaderClaimButton
|
||||
Layout.preferredWidth: 140
|
||||
text: qsTr("Claim")
|
||||
onClicked: root.claimLeaderRewardsRequested()
|
||||
}
|
||||
|
||||
LogosButton {
|
||||
Layout.fillWidth: true
|
||||
enabled: true
|
||||
padding: Theme.spacing.small
|
||||
contentItem: RowLayout {
|
||||
width: parent.width
|
||||
anchors.centerIn: parent
|
||||
LogosText {
|
||||
id: leaderClaimResultText
|
||||
Layout.fillWidth: true
|
||||
color: Theme.palette.textSecondary
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.weight: Theme.typography.weightMedium
|
||||
wrapMode: Text.WordWrap
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
LogosCopyButton {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.preferredHeight: 40
|
||||
Layout.preferredWidth: 40
|
||||
onCopyText: root.copyToClipboard(leaderClaimResultText.text)
|
||||
visible: leaderClaimResultText.text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component StyledAddressComboBox: ComboBox {
|
||||
id: comboControl
|
||||
|
||||
Layout.fillWidth: true
|
||||
padding: Theme.spacing.large
|
||||
editable: true
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
|
||||
background: Rectangle {
|
||||
color: Theme.palette.backgroundTertiary
|
||||
radius: Theme.spacing.radiusLarge
|
||||
border.color: Theme.palette.border
|
||||
border.width: 1
|
||||
}
|
||||
indicator: LogosText {
|
||||
id: comboIndicator
|
||||
text: "▼"
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.textSecondary
|
||||
x: comboControl.width - width - Theme.spacing.small
|
||||
y: (comboControl.height - height) / 2
|
||||
visible: comboControl.count > 0
|
||||
}
|
||||
contentItem: Item {
|
||||
implicitWidth: 200
|
||||
implicitHeight: 30
|
||||
|
||||
TextField {
|
||||
id: comboTextField
|
||||
anchors.fill: parent
|
||||
leftPadding: 0
|
||||
rightPadding: comboControl.count > 0 ? comboIndicator.width + Theme.spacing.small : Theme.spacing.small
|
||||
topPadding: 0
|
||||
bottomPadding: 0
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
text: comboControl.editText
|
||||
onTextChanged: if (text !== comboControl.editText) comboControl.editText = text
|
||||
selectByMouse: true
|
||||
color: Theme.palette.text
|
||||
background: Item { }
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
visible: comboControl.count > 0
|
||||
z: 1
|
||||
onPressed: {
|
||||
comboControl.popup.visible ? comboControl.popup.close() : comboControl.popup.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
delegate: ItemDelegate {
|
||||
id: comboDelegate
|
||||
width: comboControl.width
|
||||
contentItem: LogosText {
|
||||
width: parent.width
|
||||
height: contentHeight + Theme.spacing.large
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.bold: true
|
||||
text: (typeof model.address !== "undefined" ? model.address : modelData) || ""
|
||||
elide: Text.ElideMiddle
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
background: Rectangle {
|
||||
color: comboDelegate.highlighted ?
|
||||
Theme.palette.backgroundTertiary :
|
||||
Theme.palette.backgroundSecondary
|
||||
}
|
||||
highlighted: comboControl.highlightedIndex === index
|
||||
}
|
||||
popup: Popup {
|
||||
y: comboControl.height - 1
|
||||
width: comboControl.width
|
||||
height: contentItem.implicitHeight
|
||||
padding: 1
|
||||
|
||||
onOpened: if (comboControl.count === 0) close()
|
||||
|
||||
contentItem: ListView {
|
||||
clip: true
|
||||
implicitHeight: contentHeight
|
||||
model: comboControl.popup.visible ? comboControl.delegateModel : null
|
||||
ScrollIndicator.vertical: ScrollIndicator { }
|
||||
highlightFollowsCurrentItem: false
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: Theme.palette.backgroundSecondary
|
||||
border.color: Theme.palette.border
|
||||
border.width: 1
|
||||
radius: Theme.spacing.radiusLarge
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,9 @@
|
||||
module views
|
||||
StatusConfigView 1.0 StatusConfigView.qml
|
||||
LogsView 1.0 LogsView.qml
|
||||
WalletView 1.0 WalletView.qml
|
||||
AccountsView 1.0 AccountsView.qml
|
||||
TransferView 1.0 TransferView.qml
|
||||
LeaderRewardsView 1.0 LeaderRewardsView.qml
|
||||
GenerateConfigView 1.0 GenerateConfigView.qml
|
||||
ConfigChoiceView 1.0 ConfigChoiceView.qml
|
||||
SetConfigPathView 1.0 SetConfigPathView.qml
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user