feat(@desktop/wallet): Implement Transaction details as per new design
fixes #7214
This commit is contained in:
parent
cdfbb4ac87
commit
928e1999d9
|
@ -61,6 +61,9 @@ QtObject:
|
||||||
proc hex2Eth*(self: Utils, value: string): string {.slot.} =
|
proc hex2Eth*(self: Utils, value: string): string {.slot.} =
|
||||||
return stripTrailingZeroes(conversion.wei2Eth(stint.fromHex(StUint[256], value)))
|
return stripTrailingZeroes(conversion.wei2Eth(stint.fromHex(StUint[256], value)))
|
||||||
|
|
||||||
|
proc hex2Gwei*(self: Utils, value: string): string {.slot.} =
|
||||||
|
return stripTrailingZeroes(conversion.wei2Eth(stint.fromHex(StUint[256], value)*1000000000))
|
||||||
|
|
||||||
proc gwei2Hex*(self: Utils, gwei: float): string {.slot.} =
|
proc gwei2Hex*(self: Utils, gwei: float): string {.slot.} =
|
||||||
return "0x" & conversion.gwei2Wei(gwei).toHex()
|
return "0x" & conversion.gwei2Wei(gwei).toHex()
|
||||||
|
|
||||||
|
|
|
@ -113,3 +113,6 @@ proc getChainIdForBrowser*(self: Controller): int =
|
||||||
|
|
||||||
proc getEstimatedTime*(self: Controller, chainId: int, maxFeePerGas: string): EstimatedTime =
|
proc getEstimatedTime*(self: Controller, chainId: int, maxFeePerGas: string): EstimatedTime =
|
||||||
return self.transactionService.getEstimatedTime(chainId, maxFeePerGas)
|
return self.transactionService.getEstimatedTime(chainId, maxFeePerGas)
|
||||||
|
|
||||||
|
proc getLastTxBlockNumber*(self: Controller): string =
|
||||||
|
return self.transactionService.getLastTxBlockNumber(self.networkService.getNetworkForBrowser().chainId)
|
||||||
|
|
|
@ -72,3 +72,6 @@ method getEstimatedTime*(self: AccessInterface, chainId: int, maxFeePerGas: stri
|
||||||
# inheritance, which is not well supported in Nim.
|
# inheritance, which is not well supported in Nim.
|
||||||
method viewDidLoad*(self: AccessInterface) {.base.} =
|
method viewDidLoad*(self: AccessInterface) {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method getLastTxBlockNumber*(self: AccessInterface): string {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
|
@ -24,6 +24,9 @@ type
|
||||||
txHash: string
|
txHash: string
|
||||||
multiTransactionID: int
|
multiTransactionID: int
|
||||||
isTimeStamp: bool
|
isTimeStamp: bool
|
||||||
|
baseGasFees: string
|
||||||
|
totalFees: string
|
||||||
|
maxTotalFees: string
|
||||||
|
|
||||||
proc initItem*(
|
proc initItem*(
|
||||||
id: string,
|
id: string,
|
||||||
|
@ -47,7 +50,10 @@ proc initItem*(
|
||||||
input: string,
|
input: string,
|
||||||
txHash: string,
|
txHash: string,
|
||||||
multiTransactionID: int,
|
multiTransactionID: int,
|
||||||
isTimeStamp: bool
|
isTimeStamp: bool,
|
||||||
|
baseGasFees: string,
|
||||||
|
totalFees: string,
|
||||||
|
maxTotalFees: string
|
||||||
): Item =
|
): Item =
|
||||||
result.id = id
|
result.id = id
|
||||||
result.typ = typ
|
result.typ = typ
|
||||||
|
@ -71,6 +77,9 @@ proc initItem*(
|
||||||
result.txHash = txHash
|
result.txHash = txHash
|
||||||
result.multiTransactionID = multiTransactionID
|
result.multiTransactionID = multiTransactionID
|
||||||
result.isTimeStamp = isTimeStamp
|
result.isTimeStamp = isTimeStamp
|
||||||
|
result.baseGasFees = baseGasFees
|
||||||
|
result.totalFees = totalFees
|
||||||
|
result.maxTotalFees = maxTotalFees
|
||||||
|
|
||||||
proc `$`*(self: Item): string =
|
proc `$`*(self: Item): string =
|
||||||
result = fmt"""AllTokensItem(
|
result = fmt"""AllTokensItem(
|
||||||
|
@ -96,6 +105,9 @@ proc `$`*(self: Item): string =
|
||||||
txHash: {self.txHash},
|
txHash: {self.txHash},
|
||||||
multiTransactionID: {self.multiTransactionID},
|
multiTransactionID: {self.multiTransactionID},
|
||||||
isTimeStamp: {self.isTimeStamp},
|
isTimeStamp: {self.isTimeStamp},
|
||||||
|
baseGasFees: {self.baseGasFees},
|
||||||
|
totalFees: {self.totalFees},
|
||||||
|
maxTotalFees: {self.maxTotalFees},
|
||||||
]"""
|
]"""
|
||||||
|
|
||||||
proc getId*(self: Item): string =
|
proc getId*(self: Item): string =
|
||||||
|
@ -163,3 +175,12 @@ proc getMultiTransactionID*(self: Item): int =
|
||||||
|
|
||||||
proc getIsTimeStamp*(self: Item): bool =
|
proc getIsTimeStamp*(self: Item): bool =
|
||||||
return self.isTimeStamp
|
return self.isTimeStamp
|
||||||
|
|
||||||
|
proc getBaseGasFees*(self: Item): string =
|
||||||
|
return self.baseGasFees
|
||||||
|
|
||||||
|
proc getTotalFees*(self: Item): string =
|
||||||
|
return self.totalFees
|
||||||
|
|
||||||
|
proc getMaxTotalFees*(self: Item): string =
|
||||||
|
return self.maxTotalFees
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import NimQml, Tables, strutils, strformat, sequtils, tables, sugar, algorithm, std/[times, os], stint
|
import NimQml, Tables, strutils, strformat, sequtils, tables, sugar, algorithm, std/[times, os], stint, parseutils
|
||||||
|
|
||||||
import ./item
|
import ./item
|
||||||
import ../../../../../app_service/service/eth/utils as eth_service_utils
|
import ../../../../../app_service/service/eth/utils as eth_service_utils
|
||||||
|
@ -28,6 +28,9 @@ type
|
||||||
TxHash
|
TxHash
|
||||||
MultiTransactionID
|
MultiTransactionID
|
||||||
IsTimeStamp
|
IsTimeStamp
|
||||||
|
BaseGasFees
|
||||||
|
TotalFees
|
||||||
|
MaxTotalFees
|
||||||
|
|
||||||
QtObject:
|
QtObject:
|
||||||
type
|
type
|
||||||
|
@ -88,7 +91,10 @@ QtObject:
|
||||||
ModelRole.Input.int:"input",
|
ModelRole.Input.int:"input",
|
||||||
ModelRole.TxHash.int:"txHash",
|
ModelRole.TxHash.int:"txHash",
|
||||||
ModelRole.MultiTransactionID.int:"multiTransactionID",
|
ModelRole.MultiTransactionID.int:"multiTransactionID",
|
||||||
ModelRole.IsTimeStamp.int: "isTimeStamp"
|
ModelRole.IsTimeStamp.int: "isTimeStamp",
|
||||||
|
ModelRole.BaseGasFees.int: "baseGasFees",
|
||||||
|
ModelRole.TotalFees.int: "totalFees",
|
||||||
|
ModelRole.MaxTotalFees.int: "maxTotalFees"
|
||||||
}.toTable
|
}.toTable
|
||||||
|
|
||||||
method data(self: Model, index: QModelIndex, role: int): QVariant =
|
method data(self: Model, index: QModelIndex, role: int): QVariant =
|
||||||
|
@ -145,7 +151,13 @@ QtObject:
|
||||||
of ModelRole.MultiTransactionID:
|
of ModelRole.MultiTransactionID:
|
||||||
result = newQVariant(item.getMultiTransactionID())
|
result = newQVariant(item.getMultiTransactionID())
|
||||||
of ModelRole.IsTimeStamp:
|
of ModelRole.IsTimeStamp:
|
||||||
result = newQVariant(item.getIsTimeStamp())
|
result = newQVariant(item.getIsTimeStamp())
|
||||||
|
of ModelRole.BaseGasFees:
|
||||||
|
result = newQVariant(item.getBaseGasFees())
|
||||||
|
of ModelRole.TotalFees:
|
||||||
|
result = newQVariant(item.getTotalFees())
|
||||||
|
of ModelRole.MaxTotalFees:
|
||||||
|
result = newQVariant(item.getMaxTotalFees())
|
||||||
|
|
||||||
proc setItems*(self: Model, items: seq[Item]) =
|
proc setItems*(self: Model, items: seq[Item]) =
|
||||||
self.beginResetModel()
|
self.beginResetModel()
|
||||||
|
@ -206,7 +218,10 @@ QtObject:
|
||||||
t.input,
|
t.input,
|
||||||
t.txHash,
|
t.txHash,
|
||||||
t.multiTransactionID,
|
t.multiTransactionID,
|
||||||
false
|
false,
|
||||||
|
t.baseGasFees,
|
||||||
|
t.totalFees,
|
||||||
|
t.maxTotalFees,
|
||||||
))
|
))
|
||||||
|
|
||||||
var allTxs = self.items.concat(newTxItems)
|
var allTxs = self.items.concat(newTxItems)
|
||||||
|
@ -219,7 +234,7 @@ QtObject:
|
||||||
for tx in allTxs:
|
for tx in allTxs:
|
||||||
let duration = fromUnix(tx.getTimestamp()) - tempTimeStamp
|
let duration = fromUnix(tx.getTimestamp()) - tempTimeStamp
|
||||||
if(duration.inDays != 0):
|
if(duration.inDays != 0):
|
||||||
itemsWithDateHeaders.add(initItem("", "", "", "", "", tx.getTimestamp(), "", "", "", "", "", "", "", "", "", 0, "", "", "", "", 0, true))
|
itemsWithDateHeaders.add(initItem("", "", "", "", "", tx.getTimestamp(), "", "", "", "", "", "", "", "", "", 0, "", "", "", "", 0, true, "", "", ""))
|
||||||
itemsWithDateHeaders.add(tx)
|
itemsWithDateHeaders.add(tx)
|
||||||
tempTimeStamp = fromUnix(tx.getTimestamp())
|
tempTimeStamp = fromUnix(tx.getTimestamp())
|
||||||
|
|
||||||
|
|
|
@ -113,3 +113,6 @@ method getChainIdForBrowser*(self: Module): int =
|
||||||
|
|
||||||
method getEstimatedTime*(self: Module, chainId: int, maxFeePerGas: string): int =
|
method getEstimatedTime*(self: Module, chainId: int, maxFeePerGas: string): int =
|
||||||
return self.controller.getEstimatedTime(chainId, maxFeePerGas).int
|
return self.controller.getEstimatedTime(chainId, maxFeePerGas).int
|
||||||
|
|
||||||
|
method getLastTxBlockNumber*(self: Module): string =
|
||||||
|
return self.controller.getLastTxBlockNumber()
|
||||||
|
|
|
@ -150,3 +150,6 @@ QtObject:
|
||||||
|
|
||||||
proc getEstimatedTime*(self: View, chainId: int, maxFeePerGas: string): int {.slot.} =
|
proc getEstimatedTime*(self: View, chainId: int, maxFeePerGas: string): int {.slot.} =
|
||||||
return self.delegate.getEstimatedTime(chainId, maxFeePerGas)
|
return self.delegate.getEstimatedTime(chainId, maxFeePerGas)
|
||||||
|
|
||||||
|
proc getLastTxBlockNumber*(self: View): string {.slot.} =
|
||||||
|
return self.delegate.getLastTxBlockNumber()
|
||||||
|
|
|
@ -49,6 +49,21 @@ type
|
||||||
input*: string
|
input*: string
|
||||||
txHash*: string
|
txHash*: string
|
||||||
multiTransactionID*: int
|
multiTransactionID*: int
|
||||||
|
baseGasFees*: string
|
||||||
|
totalFees*: string
|
||||||
|
maxTotalFees*: string
|
||||||
|
|
||||||
|
|
||||||
|
proc getTotalFees(tip: string, baseFee: string, gasUsed: string, maxFee: string): string =
|
||||||
|
var maxFees = stint.fromHex(Uint256, maxFee)
|
||||||
|
var totalGasUsed = stint.fromHex(Uint256, tip) + stint.fromHex(Uint256, baseFee)
|
||||||
|
if totalGasUsed > maxFees:
|
||||||
|
totalGasUsed = maxFees
|
||||||
|
var totalGasUsedInHex = (totalGasUsed * stint.fromHex(Uint256, gasUsed)).toHex
|
||||||
|
return totalGasUsedInHex
|
||||||
|
|
||||||
|
proc getMaxTotalFees(maxFee: string, gasLimit: string): string =
|
||||||
|
return (stint.fromHex(Uint256, maxFee) * stint.fromHex(Uint256, gasLimit)).toHex
|
||||||
|
|
||||||
proc toTransactionDto*(jsonObj: JsonNode): TransactionDto =
|
proc toTransactionDto*(jsonObj: JsonNode): TransactionDto =
|
||||||
result = TransactionDto()
|
result = TransactionDto()
|
||||||
|
@ -73,6 +88,9 @@ proc toTransactionDto*(jsonObj: JsonNode): TransactionDto =
|
||||||
discard jsonObj.getProp("input", result.input)
|
discard jsonObj.getProp("input", result.input)
|
||||||
discard jsonObj.getProp("txHash", result.txHash)
|
discard jsonObj.getProp("txHash", result.txHash)
|
||||||
discard jsonObj.getProp("multiTransactionID", result.multiTransactionID)
|
discard jsonObj.getProp("multiTransactionID", result.multiTransactionID)
|
||||||
|
discard jsonObj.getProp("base_gas_fee", result.baseGasFees)
|
||||||
|
result.totalFees = getTotalFees(result.maxPriorityFeePerGas, result.baseGasFees, result.gasUsed, result.maxFeePerGas)
|
||||||
|
result.maxTotalFees = getMaxTotalFees(result.maxFeePerGas, result.gasLimit)
|
||||||
|
|
||||||
proc cmpTransactions*(x, y: TransactionDto): int =
|
proc cmpTransactions*(x, y: TransactionDto): int =
|
||||||
# Sort proc to compare transactions from a single account.
|
# Sort proc to compare transactions from a single account.
|
||||||
|
|
|
@ -422,3 +422,11 @@ QtObject:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error "Error estimating transaction time", message = e.msg
|
error "Error estimating transaction time", message = e.msg
|
||||||
return EstimatedTime.Unknown
|
return EstimatedTime.Unknown
|
||||||
|
|
||||||
|
proc getLastTxBlockNumber*(self: Service, chainId: int): string =
|
||||||
|
try:
|
||||||
|
let response = eth.getBlockByNumber(chainId, "latest")
|
||||||
|
return response.result{"number"}.getStr
|
||||||
|
except Exception as e:
|
||||||
|
error "Error getting latest block number", message = e.msg
|
||||||
|
return ""
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 7108ac87cffe529d011ec1c14473630e52980282
|
Subproject commit c3bbb396ecd5ad25df565d64429aaba869df4b49
|
|
@ -54,6 +54,7 @@ Item {
|
||||||
id: walletContainer
|
id: walletContainer
|
||||||
RightTabView {
|
RightTabView {
|
||||||
store: root.store
|
store: root.store
|
||||||
|
contactsStore: root.contactsStore
|
||||||
sendModal: root.sendModal
|
sendModal: root.sendModal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import StatusQ.Controls 0.1
|
||||||
import utils 1.0
|
import utils 1.0
|
||||||
import shared.views 1.0
|
import shared.views 1.0
|
||||||
|
|
||||||
|
import "./"
|
||||||
import "../stores"
|
import "../stores"
|
||||||
import "../panels"
|
import "../panels"
|
||||||
import "../views/collectibles"
|
import "../views/collectibles"
|
||||||
|
@ -15,6 +16,7 @@ Item {
|
||||||
|
|
||||||
property alias currentTabIndex: walletTabBar.currentIndex
|
property alias currentTabIndex: walletTabBar.currentIndex
|
||||||
property var store
|
property var store
|
||||||
|
property var contactsStore
|
||||||
property var sendModal
|
property var sendModal
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
|
@ -80,6 +82,10 @@ Item {
|
||||||
}
|
}
|
||||||
HistoryView {
|
HistoryView {
|
||||||
account: RootStore.currentAccount
|
account: RootStore.currentAccount
|
||||||
|
onLaunchTransactionDetail: {
|
||||||
|
transactionDetailView.transaction = transaction
|
||||||
|
stack.currentIndex = 3
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,6 +100,14 @@ Item {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
onGoBack: stack.currentIndex = 0
|
onGoBack: stack.currentIndex = 0
|
||||||
}
|
}
|
||||||
|
TransactionDetailView {
|
||||||
|
id: transactionDetailView
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
sendModal: root.sendModal
|
||||||
|
contactsStore: root.contactsStore
|
||||||
|
onGoBack: stack.currentIndex = 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WalletFooter {
|
WalletFooter {
|
||||||
|
|
|
@ -26,6 +26,16 @@ Item {
|
||||||
id: _internal
|
id: _internal
|
||||||
property bool loading: false
|
property bool loading: false
|
||||||
property string error: ""
|
property string error: ""
|
||||||
|
function saveAddress(name, address) {
|
||||||
|
loading = true
|
||||||
|
error = RootStore.createOrUpdateSavedAddress(name, address)
|
||||||
|
loading = false
|
||||||
|
}
|
||||||
|
function deleteSavedAddress(address) {
|
||||||
|
loading = true
|
||||||
|
error = RootStore.deleteSavedAddress(address)
|
||||||
|
loading = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
@ -65,155 +75,6 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
|
||||||
id: delegateSavedAddress
|
|
||||||
StatusListItem {
|
|
||||||
id: savedAddress
|
|
||||||
title: name
|
|
||||||
objectName: name
|
|
||||||
subTitle: name + " \u2022 " + Utils.getElidedCompressedPk(address)
|
|
||||||
implicitWidth: parent.width
|
|
||||||
color: "transparent"
|
|
||||||
border.color: Theme.palette.baseColor5
|
|
||||||
//TODO uncomment when #6456 is fixed
|
|
||||||
//titleTextIcon: RootStore.favouriteAddress ? "star-icon" : ""
|
|
||||||
statusListItemComponentsSlot.spacing: 0
|
|
||||||
property bool showButtons: sensor.containsMouse
|
|
||||||
|
|
||||||
components: [
|
|
||||||
StatusRoundButton {
|
|
||||||
icon.color: savedAddress.showButtons ? Theme.palette.directColor1 : Theme.palette.baseColor1
|
|
||||||
type: StatusRoundButton.Type.Tertiary
|
|
||||||
icon.name: "send"
|
|
||||||
onClicked: {
|
|
||||||
root.sendModal.open(address);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
CopyToClipBoardButton {
|
|
||||||
type: StatusRoundButton.Type.Tertiary
|
|
||||||
icon.color: savedAddress.showButtons ? Theme.palette.directColor1 : Theme.palette.baseColor1
|
|
||||||
store: RootStore
|
|
||||||
textToCopy: address
|
|
||||||
},
|
|
||||||
//TODO uncomment when #6456 is fixed
|
|
||||||
// StatusRoundButton {
|
|
||||||
// icon.color: savedAddress.showButtons ? Theme.palette.directColor1 : Theme.palette.baseColor1
|
|
||||||
// type: StatusRoundButton.Type.Tertiary
|
|
||||||
// icon.name: savedAddress.favouriteAddress ? "favourite" : "unfavourite"
|
|
||||||
// onClicked: {
|
|
||||||
// RootStore.setFavourite();
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
StatusRoundButton {
|
|
||||||
icon.color: savedAddress.showButtons ? Theme.palette.directColor1 : Theme.palette.baseColor1
|
|
||||||
type: StatusRoundButton.Type.Tertiary
|
|
||||||
icon.name: "more"
|
|
||||||
onClicked: {
|
|
||||||
editDeleteMenu.openMenu(name, address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusPopupMenu {
|
|
||||||
id: editDeleteMenu
|
|
||||||
property string contactName
|
|
||||||
property string contactAddress
|
|
||||||
function openMenu(name, address) {
|
|
||||||
contactName = name;
|
|
||||||
contactAddress = address;
|
|
||||||
popup();
|
|
||||||
}
|
|
||||||
onClosed: {
|
|
||||||
contactName = "";
|
|
||||||
contactAddress = "";
|
|
||||||
}
|
|
||||||
StatusMenuItem {
|
|
||||||
text: qsTr("Edit")
|
|
||||||
objectName: "editSavedAddress"
|
|
||||||
icon.name: "pencil-outline"
|
|
||||||
onTriggered: {
|
|
||||||
Global.openPopup(addEditSavedAddress,
|
|
||||||
{
|
|
||||||
edit: true,
|
|
||||||
address: editDeleteMenu.contactAddress,
|
|
||||||
name: editDeleteMenu.contactName
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
StatusMenuSeparator { }
|
|
||||||
StatusMenuItem {
|
|
||||||
text: qsTr("Delete")
|
|
||||||
type: StatusMenuItem.Type.Danger
|
|
||||||
icon.name: "delete"
|
|
||||||
objectName: "deleteSavedAddress"
|
|
||||||
onTriggered: {
|
|
||||||
deleteAddressConfirm.name = editDeleteMenu.contactName;
|
|
||||||
deleteAddressConfirm.address = editDeleteMenu.contactAddress;
|
|
||||||
deleteAddressConfirm.open()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: addEditSavedAddress
|
|
||||||
AddEditSavedAddressPopup {
|
|
||||||
id: addEditModal
|
|
||||||
anchors.centerIn: parent
|
|
||||||
onClosed: destroy()
|
|
||||||
contactsStore: root.contactsStore
|
|
||||||
onSave: {
|
|
||||||
_internal.loading = true
|
|
||||||
_internal.error = RootStore.createOrUpdateSavedAddress(name, address)
|
|
||||||
_internal.loading = false
|
|
||||||
close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusModal {
|
|
||||||
id: deleteAddressConfirm
|
|
||||||
property string address
|
|
||||||
property string name
|
|
||||||
// NOTE: the `text` property was created as a workaround because
|
|
||||||
// setting StatusBaseText.text to `qsTr("...").arg("...")`
|
|
||||||
// caused no text to render
|
|
||||||
property string text: qsTr("Are you sure you want to remove '%1' from your saved addresses?").arg(name)
|
|
||||||
anchors.centerIn: parent
|
|
||||||
header.title: qsTr("Are you sure?")
|
|
||||||
header.subTitle: name
|
|
||||||
contentItem: StatusBaseText {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
height: contentHeight + topPadding + bottomPadding
|
|
||||||
text: deleteAddressConfirm.text
|
|
||||||
font.pixelSize: 15
|
|
||||||
color: Theme.palette.directColor1
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
topPadding: Style.current.padding
|
|
||||||
rightPadding: Style.current.padding
|
|
||||||
bottomPadding: Style.current.padding
|
|
||||||
leftPadding: Style.current.padding
|
|
||||||
}
|
|
||||||
rightButtons: [
|
|
||||||
StatusButton {
|
|
||||||
text: qsTr("Cancel")
|
|
||||||
onClicked: deleteAddressConfirm.close()
|
|
||||||
},
|
|
||||||
StatusButton {
|
|
||||||
type: StatusBaseButton.Type.Danger
|
|
||||||
objectName: "confirmDeleteSavedAddress"
|
|
||||||
text: qsTr("Delete")
|
|
||||||
onClicked: {
|
|
||||||
_internal.loading = true
|
|
||||||
_internal.error = RootStore.deleteSavedAddress(deleteAddressConfirm.address)
|
|
||||||
deleteAddressConfirm.close()
|
|
||||||
_internal.loading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
SavedAddressesError {
|
SavedAddressesError {
|
||||||
id: errorMessage
|
id: errorMessage
|
||||||
anchors.top: header.bottom
|
anchors.top: header.bottom
|
||||||
|
@ -242,6 +103,32 @@ Item {
|
||||||
visible: listView.count > 0
|
visible: listView.count > 0
|
||||||
spacing: 5
|
spacing: 5
|
||||||
model: RootStore.savedAddresses
|
model: RootStore.savedAddresses
|
||||||
delegate: delegateSavedAddress
|
delegate: SavedAddressesDelegate {
|
||||||
|
name: model.name
|
||||||
|
address: model.address
|
||||||
|
store: RootStore
|
||||||
|
contactsStore: root.contactsStore
|
||||||
|
onOpenSendModal: root.sendModal.open(address);
|
||||||
|
saveAddress: function(name, address) {
|
||||||
|
_internal.saveAddress(name, address)
|
||||||
|
}
|
||||||
|
deleteSavedAddress: function(address) {
|
||||||
|
_internal.deleteSavedAddress(address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: addEditSavedAddress
|
||||||
|
AddEditSavedAddressPopup {
|
||||||
|
id: addEditModal
|
||||||
|
anchors.centerIn: parent
|
||||||
|
onClosed: destroy()
|
||||||
|
contactsStore: root.contactsStore
|
||||||
|
onSave: {
|
||||||
|
_internal.saveAddress(name, address)
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,176 @@
|
||||||
|
import QtQuick 2.13
|
||||||
|
import QtQuick.Controls 2.13
|
||||||
|
|
||||||
|
import utils 1.0
|
||||||
|
|
||||||
|
import StatusQ.Controls 0.1
|
||||||
|
import StatusQ.Components 0.1
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
import StatusQ.Popups 0.1
|
||||||
|
import shared.controls 1.0
|
||||||
|
|
||||||
|
import "../popups"
|
||||||
|
import "../controls"
|
||||||
|
|
||||||
|
StatusListItem {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var store
|
||||||
|
property var contactsStore
|
||||||
|
property string name
|
||||||
|
property string address
|
||||||
|
property var saveAddress: function (name, address) {}
|
||||||
|
property var deleteSavedAddress: function (address) {}
|
||||||
|
|
||||||
|
signal openSendModal()
|
||||||
|
|
||||||
|
implicitWidth: parent.width
|
||||||
|
|
||||||
|
title: name
|
||||||
|
objectName: name
|
||||||
|
subTitle: name + " \u2022 " + Utils.getElidedCompressedPk(address)
|
||||||
|
color: "transparent"
|
||||||
|
border.color: Theme.palette.baseColor5
|
||||||
|
//TODO uncomment when #6456 is fixed
|
||||||
|
//titleTextIcon: RootStore.favouriteAddress ? "star-icon" : ""
|
||||||
|
statusListItemComponentsSlot.spacing: 0
|
||||||
|
property bool showButtons: sensor.containsMouse
|
||||||
|
|
||||||
|
components: [
|
||||||
|
StatusRoundButton {
|
||||||
|
icon.color: root.showButtons ? Theme.palette.directColor1 : Theme.palette.baseColor1
|
||||||
|
type: StatusRoundButton.Type.Tertiary
|
||||||
|
icon.name: "send"
|
||||||
|
onClicked: openSendModal()
|
||||||
|
},
|
||||||
|
CopyToClipBoardButton {
|
||||||
|
id: copyButton
|
||||||
|
type: StatusRoundButton.Type.Tertiary
|
||||||
|
icon.color: root.showButtons ? Theme.palette.directColor1 : Theme.palette.baseColor1
|
||||||
|
store: root.store
|
||||||
|
textToCopy: root.address
|
||||||
|
},
|
||||||
|
//TODO uncomment when #6456 is fixed
|
||||||
|
// StatusRoundButton {
|
||||||
|
// icon.color: root.showButtons ? Theme.palette.directColor1 : Theme.palette.baseColor1
|
||||||
|
// type: StatusRoundButton.Type.Tertiary
|
||||||
|
// icon.name: root.favouriteAddress ? "favourite" : "unfavourite"
|
||||||
|
// onClicked: {
|
||||||
|
// RootStore.setFavourite();
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
StatusRoundButton {
|
||||||
|
visible: !!root.name
|
||||||
|
icon.color: root.showButtons ? Theme.palette.directColor1 : Theme.palette.baseColor1
|
||||||
|
type: StatusRoundButton.Type.Tertiary
|
||||||
|
icon.name: "more"
|
||||||
|
onClicked: {
|
||||||
|
editDeleteMenu.openMenu(root.name, root.address);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
StatusRoundButton {
|
||||||
|
visible: !root.name
|
||||||
|
icon.color: root.showButtons ? Theme.palette.directColor1 : Theme.palette.baseColor1
|
||||||
|
type: StatusRoundButton.Type.Tertiary
|
||||||
|
icon.name: "add"
|
||||||
|
onClicked: {
|
||||||
|
Global.openPopup(addEditSavedAddress,
|
||||||
|
{
|
||||||
|
addAddress: true,
|
||||||
|
address: root.address
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
StatusPopupMenu {
|
||||||
|
id: editDeleteMenu
|
||||||
|
property string contactName
|
||||||
|
property string contactAddress
|
||||||
|
function openMenu(name, address) {
|
||||||
|
contactName = name;
|
||||||
|
contactAddress = address;
|
||||||
|
popup();
|
||||||
|
}
|
||||||
|
onClosed: {
|
||||||
|
contactName = "";
|
||||||
|
contactAddress = "";
|
||||||
|
}
|
||||||
|
StatusMenuItem {
|
||||||
|
text: qsTr("Edit")
|
||||||
|
objectName: "editroot"
|
||||||
|
assetSettings.name: "pencil-outline"
|
||||||
|
onTriggered: {
|
||||||
|
Global.openPopup(addEditSavedAddress,
|
||||||
|
{
|
||||||
|
edit: true,
|
||||||
|
address: editDeleteMenu.contactAddress,
|
||||||
|
name: editDeleteMenu.contactName
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StatusMenuSeparator { }
|
||||||
|
StatusMenuItem {
|
||||||
|
text: qsTr("Delete")
|
||||||
|
type: StatusMenuItem.Type.Danger
|
||||||
|
assetSettings.name: "delete"
|
||||||
|
objectName: "deleteSavedAddress"
|
||||||
|
onTriggered: {
|
||||||
|
deleteAddressConfirm.name = editDeleteMenu.contactName;
|
||||||
|
deleteAddressConfirm.address = editDeleteMenu.contactAddress;
|
||||||
|
deleteAddressConfirm.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: addEditSavedAddress
|
||||||
|
AddEditSavedAddressPopup {
|
||||||
|
id: addEditModal
|
||||||
|
anchors.centerIn: parent
|
||||||
|
onClosed: destroy()
|
||||||
|
contactsStore: root.contactsStore
|
||||||
|
onSave: {
|
||||||
|
root.saveAddress(name, address)
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusModal {
|
||||||
|
id: deleteAddressConfirm
|
||||||
|
property string address
|
||||||
|
property string name
|
||||||
|
anchors.centerIn: parent
|
||||||
|
header.title: qsTr("Are you sure?")
|
||||||
|
header.subTitle: name
|
||||||
|
contentItem: StatusBaseText {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
height: contentHeight + topPadding + bottomPadding
|
||||||
|
text: qsTr("Are you sure you want to remove '%1' from your saved addresses?").arg(name)
|
||||||
|
font.pixelSize: 15
|
||||||
|
color: Theme.palette.directColor1
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
topPadding: Style.current.padding
|
||||||
|
rightPadding: Style.current.padding
|
||||||
|
bottomPadding: Style.current.padding
|
||||||
|
leftPadding: Style.current.padding
|
||||||
|
}
|
||||||
|
rightButtons: [
|
||||||
|
StatusButton {
|
||||||
|
text: qsTr("Cancel")
|
||||||
|
onClicked: deleteAddressConfirm.close()
|
||||||
|
},
|
||||||
|
StatusButton {
|
||||||
|
type: StatusBaseButton.Type.Danger
|
||||||
|
objectName: "confirmDeleteSavedAddress"
|
||||||
|
text: qsTr("Delete")
|
||||||
|
onClicked: {
|
||||||
|
root.deleteSavedAddress(deleteAddressConfirm.address)
|
||||||
|
deleteAddressConfirm.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,12 +25,13 @@ StatusListItem {
|
||||||
property string resolvedSymbol: root.symbol != "" ? root.symbol : "ETH"
|
property string resolvedSymbol: root.symbol != "" ? root.symbol : "ETH"
|
||||||
property string savedAddressName
|
property string savedAddressName
|
||||||
|
|
||||||
|
state: "normal"
|
||||||
asset.isImage: true
|
asset.isImage: true
|
||||||
asset.name: Style.png("tokens/%1".arg(resolvedSymbol))
|
asset.name: Style.png("tokens/%1".arg(resolvedSymbol))
|
||||||
statusListItemTitle.font.weight: Font.Medium
|
title: modelData !== undefined && !!modelData ?
|
||||||
title: isIncoming ? qsTr("Receive %1").arg(resolvedSymbol) : !!savedAddressName ?
|
isIncoming ? qsTr("Receive %1").arg(resolvedSymbol) : !!savedAddressName ?
|
||||||
qsTr("Send %1 to %2").arg(resolvedSymbol).arg(savedAddressName) :
|
qsTr("Send %1 to %2").arg(resolvedSymbol).arg(savedAddressName) :
|
||||||
qsTr("Send %1 to %2").arg(resolvedSymbol).arg(Utils.compactAddress(modelData.to, 4))
|
qsTr("Send %1 to %2").arg(resolvedSymbol).arg(Utils.compactAddress(modelData.to, 4)): ""
|
||||||
subTitle: shortTimeStamp
|
subTitle: shortTimeStamp
|
||||||
inlineTagModel: 1
|
inlineTagModel: 1
|
||||||
inlineTagDelegate: InformationTag {
|
inlineTagDelegate: InformationTag {
|
||||||
|
@ -60,8 +61,8 @@ StatusListItem {
|
||||||
height: 18
|
height: 18
|
||||||
}
|
}
|
||||||
StatusBaseText {
|
StatusBaseText {
|
||||||
|
id: cryptoValueText
|
||||||
text: "%1 %2".arg(cryptoValue).arg(resolvedSymbol)
|
text: "%1 %2".arg(cryptoValue).arg(resolvedSymbol)
|
||||||
font.pixelSize: 15
|
|
||||||
color: Theme.palette.directColor1
|
color: Theme.palette.directColor1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,4 +93,41 @@ StatusListItem {
|
||||||
height: 10
|
height: 10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "normal"
|
||||||
|
PropertyChanges {
|
||||||
|
target: asset
|
||||||
|
width: 40
|
||||||
|
height: 40
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: statusListItemTitle
|
||||||
|
font.weight: Font.Medium
|
||||||
|
font.pixelSize: 15
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: cryptoValueText
|
||||||
|
font.pixelSize: 15
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "big"
|
||||||
|
PropertyChanges {
|
||||||
|
target: asset
|
||||||
|
width: 50
|
||||||
|
height: 50
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: statusListItemTitle
|
||||||
|
font.weight: Font.Bold
|
||||||
|
font.pixelSize: 17
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: cryptoValueText
|
||||||
|
font.pixelSize: 17
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,3 +27,5 @@ InformationTile 1.0 InformationTile.qml
|
||||||
SocialLinkPreview 1.0 SocialLinkPreview.qml
|
SocialLinkPreview 1.0 SocialLinkPreview.qml
|
||||||
AssetsDetailsHeader 1.0 AssetsDetailsHeader.qml
|
AssetsDetailsHeader 1.0 AssetsDetailsHeader.qml
|
||||||
InformationTag 1.0 InformationTag.qml
|
InformationTag 1.0 InformationTag.qml
|
||||||
|
TransactionDetailsHeader.qml 1.0 TransactionDetailsHeader.qml
|
||||||
|
SavedAddressesDelegate 1.0 SavedAddressesDelegate.qml
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
import QtQuick 2.13
|
||||||
|
import QtQuick.Controls 2.13
|
||||||
|
import QtQml.Models 2.14
|
||||||
|
|
||||||
|
import utils 1.0
|
||||||
|
import shared.controls 1.0
|
||||||
|
import shared.panels 1.0
|
||||||
|
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
import StatusQ.Controls 0.1
|
||||||
|
import StatusQ.Controls.Validators 0.1
|
||||||
|
import StatusQ.Popups.Dialog 0.1
|
||||||
|
|
||||||
|
import "../stores"
|
||||||
|
|
||||||
|
StatusDialog {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool edit: false
|
||||||
|
property bool addAddress: false
|
||||||
|
property string address
|
||||||
|
property alias name: nameInput.text
|
||||||
|
property var contactsStore
|
||||||
|
|
||||||
|
signal save(string name, string address)
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: d
|
||||||
|
property int validationMode: root.edit ?
|
||||||
|
StatusInput.ValidationMode.Always
|
||||||
|
: StatusInput.ValidationMode.OnlyWhenDirty
|
||||||
|
property bool valid: addressInput.isValid && nameInput.valid // TODO: Add network preference and emoji
|
||||||
|
property bool dirty: nameInput.input.dirty
|
||||||
|
}
|
||||||
|
|
||||||
|
width: 574
|
||||||
|
height: 490
|
||||||
|
|
||||||
|
header: StatusDialogHeader {
|
||||||
|
headline.title: edit ? qsTr("Edit saved address") : qsTr("Add saved address")
|
||||||
|
headline.subtitle: edit ? name : ""
|
||||||
|
}
|
||||||
|
|
||||||
|
onOpened: {
|
||||||
|
if(edit || addAddress) {
|
||||||
|
addressInput.input.text = root.address
|
||||||
|
}
|
||||||
|
nameInput.input.edit.forceActiveFocus(Qt.MouseFocusReason)
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
height: childrenRect.height
|
||||||
|
topPadding: Style.current.xlPadding
|
||||||
|
|
||||||
|
spacing: Style.current.bigPadding
|
||||||
|
|
||||||
|
StatusInput {
|
||||||
|
id: nameInput
|
||||||
|
implicitWidth: parent.width
|
||||||
|
input.edit.objectName: "savedAddressNameInput"
|
||||||
|
minimumHeight: 56
|
||||||
|
maximumHeight: 56
|
||||||
|
placeholderText: qsTr("Enter a name")
|
||||||
|
label: qsTr("Name")
|
||||||
|
validators: [
|
||||||
|
StatusMinLengthValidator {
|
||||||
|
minLength: 1
|
||||||
|
errorMessage: qsTr("Name must not be blank")
|
||||||
|
},
|
||||||
|
StatusRegularExpressionValidator {
|
||||||
|
regularExpression: /^[^<>]+$/
|
||||||
|
errorMessage: qsTr("This is not a valid account name")
|
||||||
|
}
|
||||||
|
]
|
||||||
|
charLimit: 40
|
||||||
|
validationMode: d.validationMode
|
||||||
|
}
|
||||||
|
|
||||||
|
// To-Do use StatusInput within the below component
|
||||||
|
RecipientSelector {
|
||||||
|
id: addressInput
|
||||||
|
implicitWidth: parent.width
|
||||||
|
inputWidth: implicitWidth
|
||||||
|
accounts: RootStore.accounts
|
||||||
|
contactsStore: root.contactsStore
|
||||||
|
label: qsTr("Address")
|
||||||
|
input.textField.objectName: "savedAddressAddressInput"
|
||||||
|
input.placeholderText: qsTr("Enter ENS Name or Ethereum Address")
|
||||||
|
labelFont.pixelSize: 15
|
||||||
|
labelFont.weight: Font.Normal
|
||||||
|
input.implicitHeight: 56
|
||||||
|
input.textField.anchors.rightMargin: 0
|
||||||
|
isSelectorVisible: false
|
||||||
|
addContactEnabled: false
|
||||||
|
onSelectedRecipientChanged: {
|
||||||
|
root.address = selectedRecipient.address
|
||||||
|
}
|
||||||
|
readOnly: root.edit || root.addAddress
|
||||||
|
wrongInputValidationError: qsTr("Please enter a valid ENS name OR Ethereum Address")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer: StatusDialogFooter {
|
||||||
|
rightButtons: ObjectModel {
|
||||||
|
StatusButton {
|
||||||
|
text: root.edit ? qsTr("Save") : qsTr("Add address")
|
||||||
|
enabled: d.valid && d.dirty
|
||||||
|
onClicked: root.save(name, address)
|
||||||
|
objectName: "addSavedAddress"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,3 +21,4 @@ ProfilePopup 1.0 ProfilePopup.qml
|
||||||
ImageCropWorkflow 1.0 ImageCropWorkflow.qml
|
ImageCropWorkflow 1.0 ImageCropWorkflow.qml
|
||||||
ImportCommunityPopup 1.0 ImportCommunityPopup.qml
|
ImportCommunityPopup 1.0 ImportCommunityPopup.qml
|
||||||
DisplayNamePopup 1.0 DisplayNamePopup.qml
|
DisplayNamePopup 1.0 DisplayNamePopup.qml
|
||||||
|
AddEditSavedAddressPopup 1.0 AddEditSavedAddressPopup.qml
|
||||||
|
|
|
@ -38,6 +38,7 @@ QtObject {
|
||||||
property var historyTransactions: walletSectionTransactions.model
|
property var historyTransactions: walletSectionTransactions.model
|
||||||
property bool isNonArchivalNode: history.isNonArchivalNode
|
property bool isNonArchivalNode: history.isNonArchivalNode
|
||||||
|
|
||||||
|
property var currentAccount: walletSectionCurrent
|
||||||
property var walletTokensModule: walletSectionAllTokens
|
property var walletTokensModule: walletSectionAllTokens
|
||||||
property var tokens: walletSectionAllTokens.all
|
property var tokens: walletSectionAllTokens.all
|
||||||
property var accounts: walletSectionAccounts.model
|
property var accounts: walletSectionAccounts.model
|
||||||
|
@ -170,6 +171,10 @@ QtObject {
|
||||||
return globalUtils.hex2Eth(value)
|
return globalUtils.hex2Eth(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hex2Gwei(value) {
|
||||||
|
return globalUtils.hex2Gwei(value)
|
||||||
|
}
|
||||||
|
|
||||||
function findTokenSymbolByAddress(address) {
|
function findTokenSymbolByAddress(address) {
|
||||||
return walletSectionAllTokens.findTokenSymbolByAddress(address)
|
return walletSectionAllTokens.findTokenSymbolByAddress(address)
|
||||||
|
|
||||||
|
@ -178,4 +183,20 @@ QtObject {
|
||||||
function getNameForSavedWalletAddress(address) {
|
function getNameForSavedWalletAddress(address) {
|
||||||
return walletSectionSavedAddresses.getNameByAddress(address)
|
return walletSectionSavedAddresses.getNameByAddress(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createOrUpdateSavedAddress(name, address) {
|
||||||
|
return walletSectionSavedAddresses.createOrUpdateSavedAddress(name, address)
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteSavedAddress(address) {
|
||||||
|
return walletSectionSavedAddresses.deleteSavedAddress(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLatestBlockNumber() {
|
||||||
|
return walletSectionTransactions.getLastTxBlockNumber()
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGasEthValue(gweiValue, gasLimit) {
|
||||||
|
return profileSectionModule.ensUsernamesModule.getGasEthValue(gweiValue, gasLimit)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@ ColumnLayout {
|
||||||
property int pageSize: 20 // number of transactions per page
|
property int pageSize: 20 // number of transactions per page
|
||||||
property bool isLoading: false
|
property bool isLoading: false
|
||||||
|
|
||||||
|
signal launchTransactionDetail(var transaction)
|
||||||
|
|
||||||
function fetchHistory() {
|
function fetchHistory() {
|
||||||
if (RootStore.isFetchingHistory(historyView.account.address)) {
|
if (RootStore.isFetchingHistory(historyView.account.address)) {
|
||||||
isLoading = true
|
isLoading = true
|
||||||
|
@ -78,7 +80,7 @@ ColumnLayout {
|
||||||
onLoaded: {
|
onLoaded: {
|
||||||
item.modelData = model
|
item.modelData = model
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollBar.vertical: StatusScrollBar {}
|
ScrollBar.vertical: StatusScrollBar {}
|
||||||
|
|
||||||
|
@ -99,7 +101,7 @@ ColumnLayout {
|
||||||
StatusListItem {
|
StatusListItem {
|
||||||
property var modelData
|
property var modelData
|
||||||
height: 40
|
height: 40
|
||||||
title: Utils.formatShortDate(modelData.timestamp * 1000, RootStore.accountSensitiveSettings.is24hTimeFormat)
|
title: modelData !== undefined && !!modelData ? Utils.formatShortDate(modelData.timestamp * 1000, RootStore.accountSensitiveSettings.is24hTimeFormat) : ""
|
||||||
statusListItemTitle.color: Theme.palette.baseColor1
|
statusListItemTitle.color: Theme.palette.baseColor1
|
||||||
color: Theme.palette.statusListItem.backgroundColor
|
color: Theme.palette.statusListItem.backgroundColor
|
||||||
sensor.enabled: false
|
sensor.enabled: false
|
||||||
|
@ -109,21 +111,18 @@ ColumnLayout {
|
||||||
Component {
|
Component {
|
||||||
id: transactionDelegate
|
id: transactionDelegate
|
||||||
TransactionDelegate {
|
TransactionDelegate {
|
||||||
isIncoming: modelData !== undefined ? modelData.to === account.address: false
|
isIncoming: modelData !== undefined && !!modelData ? modelData.to === account.address: false
|
||||||
currentCurrency: RootStore.currentCurrency
|
currentCurrency: RootStore.currentCurrency
|
||||||
cryptoValue: modelData !== undefined ? RootStore.hex2Eth(modelData.value) : ""
|
cryptoValue: modelData !== undefined && !!modelData ? RootStore.hex2Eth(modelData.value) : ""
|
||||||
fiatValue: RootStore.getFiatValue(cryptoValue, resolvedSymbol, RootStore.currentCurrency)
|
fiatValue: RootStore.getFiatValue(cryptoValue, resolvedSymbol, RootStore.currentCurrency)
|
||||||
networkIcon: modelData !== undefined ? RootStore.getNetworkIcon(modelData.chainId) : ""
|
networkIcon: modelData !== undefined && !!modelData ? RootStore.getNetworkIcon(modelData.chainId) : ""
|
||||||
networkColor: modelData !== undefined ? RootStore.getNetworkColor(modelData.chainId) : ""
|
networkColor: modelData !== undefined && !!modelData ? RootStore.getNetworkColor(modelData.chainId) : ""
|
||||||
networkName: modelData !== undefined ? RootStore.getNetworkShortName(modelData.chainId) : ""
|
networkName: modelData !== undefined && !!modelData ? RootStore.getNetworkShortName(modelData.chainId) : ""
|
||||||
symbol: modelData !== undefined ? RootStore.findTokenSymbolByAddress(modelData.contract) : ""
|
symbol: modelData !== undefined && !!modelData ? RootStore.findTokenSymbolByAddress(modelData.contract) : ""
|
||||||
transferStatus: modelData !== undefined ? RootStore.hex2Dec(modelData.txStatus) : ""
|
transferStatus: modelData !== undefined && !!modelData ? RootStore.hex2Dec(modelData.txStatus) : ""
|
||||||
shortTimeStamp: modelData !== undefined ? Utils.formatShortTime(modelData.timestamp * 1000, RootStore.accountSensitiveSettings.is24hTimeFormat) : ""
|
shortTimeStamp: modelData !== undefined && !!modelData ? Utils.formatShortTime(modelData.timestamp * 1000, RootStore.accountSensitiveSettings.is24hTimeFormat) : ""
|
||||||
savedAddressName: modelData !== undefined ? RootStore.getNameForSavedWalletAddress(modelData.to) : ""
|
savedAddressName: modelData !== undefined && !!modelData ? RootStore.getNameForSavedWalletAddress(modelData.to) : ""
|
||||||
onClicked: {
|
onClicked: launchTransactionDetail(modelData)
|
||||||
transactionModal.transaction = modelData
|
|
||||||
transactionModal.open()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,8 +130,4 @@ ColumnLayout {
|
||||||
id: loadingImageComponent
|
id: loadingImageComponent
|
||||||
StatusLoadingIndicator {}
|
StatusLoadingIndicator {}
|
||||||
}
|
}
|
||||||
|
|
||||||
TransactionModal {
|
|
||||||
id: transactionModal
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,250 @@
|
||||||
|
import QtQuick 2.13
|
||||||
|
import QtQuick.Layouts 1.13
|
||||||
|
import QtQuick.Controls 2.14
|
||||||
|
import QtQuick.Window 2.12
|
||||||
|
|
||||||
|
import StatusQ.Components 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Controls 0.1
|
||||||
|
import StatusQ.Popups 0.1
|
||||||
|
|
||||||
|
import shared.controls 1.0
|
||||||
|
import utils 1.0
|
||||||
|
|
||||||
|
import "../stores"
|
||||||
|
import "../controls"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var currentAccount: RootStore.currentAccount
|
||||||
|
property var contactsStore
|
||||||
|
property var transaction
|
||||||
|
property var sendModal
|
||||||
|
|
||||||
|
signal goBack()
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: d
|
||||||
|
readonly property bool isIncoming: root.transaction !== undefined && !!root.transaction ? root.transaction.to === currentAccount.address : false
|
||||||
|
readonly property string savedAddressNameTo: root.transaction !== undefined && !!root.transaction ? d.getNameForSavedWalletAddress(transaction.to) : ""
|
||||||
|
readonly property string savedAddressNameFrom: root.transaction !== undefined && !!root.transaction ? d.getNameForSavedWalletAddress(transaction.from): ""
|
||||||
|
readonly property string from: root.transaction !== undefined && !!root.transaction ? !!savedAddressNameFrom ? savedAddressNameFrom : Utils.compactAddress(transaction.from, 4): ""
|
||||||
|
readonly property string to: root.transaction !== undefined && !!root.transaction ? !!savedAddressNameTo ? savedAddressNameTo : Utils.compactAddress(transaction.to, 4): ""
|
||||||
|
|
||||||
|
function getNameForSavedWalletAddress(address) {
|
||||||
|
return RootStore.getNameForSavedWalletAddress(address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusFlatButton {
|
||||||
|
id: backButton
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
anchors.topMargin: -Style.current.xlPadding
|
||||||
|
anchors.leftMargin: -Style.current.xlPadding
|
||||||
|
icon.name: "arrow-left"
|
||||||
|
icon.width: 20
|
||||||
|
icon.height: 20
|
||||||
|
text: qsTr("Activity")
|
||||||
|
size: StatusBaseButton.Size.Large
|
||||||
|
onClicked: root.goBack()
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusScrollView {
|
||||||
|
anchors.top: backButton.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
contentHeight: column.height
|
||||||
|
contentWidth: parent.width
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: column
|
||||||
|
width: parent.width - Style.current.xlPadding
|
||||||
|
|
||||||
|
spacing: Style.current.bigPadding
|
||||||
|
|
||||||
|
TransactionDelegate {
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
modelData: transaction
|
||||||
|
isIncoming: d.isIncoming
|
||||||
|
currentCurrency: RootStore.currentCurrency
|
||||||
|
cryptoValue: root.transaction !== undefined && !!root.transaction ? RootStore.hex2Eth(transaction.value): ""
|
||||||
|
fiatValue: root.transaction !== undefined && !!root.transaction ? RootStore.getFiatValue(cryptoValue, resolvedSymbol, RootStore.currentCurrency): ""
|
||||||
|
networkIcon: root.transaction !== undefined && !!root.transaction ? RootStore.getNetworkIcon(transaction.chainId): ""
|
||||||
|
networkColor: root.transaction !== undefined && !!root.transaction ? RootStore.getNetworkColor(transaction.chainId): ""
|
||||||
|
networkName: root.transaction !== undefined && !!root.transaction ? RootStore.getNetworkShortName(transaction.chainId): ""
|
||||||
|
symbol: root.transaction !== undefined && !!root.transaction ? RootStore.findTokenSymbolByAddress(transaction.contract): ""
|
||||||
|
transferStatus: root.transaction !== undefined && !!root.transaction ? RootStore.hex2Dec(transaction.txStatus): ""
|
||||||
|
shortTimeStamp: root.transaction !== undefined && !!root.transaction ? Utils.formatShortTime(transaction.timestamp * 1000, RootStore.accountSensitiveSettings.is24hTimeFormat): ""
|
||||||
|
savedAddressName: root.transaction !== undefined && !!root.transaction ? RootStore.getNameForSavedWalletAddress(transaction.to): ""
|
||||||
|
title: d.isIncoming ? qsTr("Received %1 %2 from %3").arg(cryptoValue).arg(resolvedSymbol).arg(d.from) :
|
||||||
|
qsTr("Sent %1 %2 to %3").arg(cryptoValue).arg(resolvedSymbol).arg(d.to)
|
||||||
|
sensor.enabled: false
|
||||||
|
color: Theme.palette.statusListItem.backgroundColor
|
||||||
|
state: "big"
|
||||||
|
}
|
||||||
|
|
||||||
|
SavedAddressesDelegate {
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
name: d.isIncoming ? d.savedAddressNameFrom : d.savedAddressNameTo
|
||||||
|
address: root.transaction !== undefined && !!root.transaction ? d.isIncoming ? transaction.from : transaction.to : ""
|
||||||
|
title: d.isIncoming ? d.from : d.to
|
||||||
|
subTitle: root.transaction !== undefined && !!root.transaction ? d.isIncoming ? !!d.savedAddressNameFrom ? Utils.compactAddress(transaction.from, 4) : "" : !!d.savedAddressNameTo ? Utils.compactAddress(transaction.to, 4) : "": ""
|
||||||
|
store: RootStore
|
||||||
|
contactsStore: root.contactsStore
|
||||||
|
onOpenSendModal: root.sendModal.open(address);
|
||||||
|
saveAddress: function(name, address) {
|
||||||
|
RootStore.createOrUpdateSavedAddress(name, address)
|
||||||
|
}
|
||||||
|
deleteSavedAddress: function(address) {
|
||||||
|
RootStore.deleteSavedAddress(address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusExpandableItem {
|
||||||
|
width: parent.width
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
type: StatusExpandableItem.Type.Tertiary
|
||||||
|
expandable: true
|
||||||
|
primaryText: qsTr("Transaction summary")
|
||||||
|
expandableComponent: transactionSummary
|
||||||
|
separatorVisible: false
|
||||||
|
expanded: true
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusExpandableItem {
|
||||||
|
width: parent.width
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
type: StatusExpandableItem.Type.Tertiary
|
||||||
|
expandable: true
|
||||||
|
primaryText: qsTr("Fees")
|
||||||
|
expandableComponent: fees
|
||||||
|
expanded: true
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusListItem {
|
||||||
|
id: data
|
||||||
|
width: parent.width
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
color: "transparent"
|
||||||
|
border.width: 1
|
||||||
|
border.color: Theme.palette.directColor8
|
||||||
|
|
||||||
|
statusListItemTitle.color: Theme.palette.baseColor1
|
||||||
|
|
||||||
|
title: qsTr("Data" )
|
||||||
|
subTitle: root.transaction !== undefined && !!root.transaction ? root.transaction.input : ""
|
||||||
|
components: [
|
||||||
|
CopyToClipBoardButton {
|
||||||
|
icon.width: 15
|
||||||
|
icon.height: 15
|
||||||
|
type: StatusRoundButton.Type.Tertiary
|
||||||
|
color: "transparent"
|
||||||
|
icon.color: data.showButtons ? Theme.palette.directColor1 : Theme.palette.baseColor1
|
||||||
|
store: RootStore
|
||||||
|
textToCopy: data.subTitle
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: transactionSummary
|
||||||
|
Column {
|
||||||
|
id: column
|
||||||
|
width: parent.width
|
||||||
|
spacing: 8
|
||||||
|
TransactionDelegate {
|
||||||
|
width: parent.width
|
||||||
|
modelData: transaction
|
||||||
|
isIncoming: d.isIncoming
|
||||||
|
currentCurrency: RootStore.currentCurrency
|
||||||
|
cryptoValue: root.transaction !== undefined && !!root.transaction ? RootStore.hex2Eth(transaction.value): ""
|
||||||
|
fiatValue: RootStore.getFiatValue(cryptoValue, resolvedSymbol, RootStore.currentCurrency)
|
||||||
|
networkIcon: root.transaction !== undefined && !!root.transaction ? RootStore.getNetworkIcon(transaction.chainId) : ""
|
||||||
|
networkColor: root.transaction !== undefined && !!root.transaction ? RootStore.getNetworkColor(transaction.chainId): ""
|
||||||
|
networkName: root.transaction !== undefined && !!root.transaction ? RootStore.getNetworkShortName(transaction.chainId): ""
|
||||||
|
symbol: root.transaction !== undefined && !!root.transaction ? RootStore.findTokenSymbolByAddress(transaction.contract): ""
|
||||||
|
transferStatus: root.transaction !== undefined && !!root.transaction ? RootStore.hex2Dec(transaction.txStatus): ""
|
||||||
|
shortTimeStamp: root.transaction !== undefined && !!root.transaction ? Utils.formatShortTime(transaction.timestamp * 1000, RootStore.accountSensitiveSettings.is24hTimeFormat): ""
|
||||||
|
savedAddressName: root.transaction !== undefined && !!root.transaction ? RootStore.getNameForSavedWalletAddress(transaction.to): ""
|
||||||
|
title: d.isIncoming ? qsTr("Received %1 %2 from %3").arg(cryptoValue).arg(resolvedSymbol).arg(d.from) :
|
||||||
|
qsTr("Sent %1 %2 to %3").arg(cryptoValue).arg(resolvedSymbol).arg(d.to)
|
||||||
|
sensor.enabled: false
|
||||||
|
color: Theme.palette.statusListItem.backgroundColor
|
||||||
|
border.width: 1
|
||||||
|
border.color: Theme.palette.directColor8
|
||||||
|
}
|
||||||
|
Row {
|
||||||
|
spacing: 8
|
||||||
|
InformationTile {
|
||||||
|
maxWidth: parent.width
|
||||||
|
primaryText: qsTr("Time")
|
||||||
|
secondaryText: root.transaction !== undefined && !!root.transaction ? qsTr("%1 <font color=\"#939BA1\">on</font> %2").
|
||||||
|
arg(Utils.formatShortTime(transaction.timestamp * 1000, RootStore.accountSensitiveSettings.is24hTimeFormat)).
|
||||||
|
arg(Utils.formatShortDate(transaction.timestamp * 1000, RootStore.accountSensitiveSettings.is24hTimeFormat)): ""
|
||||||
|
}
|
||||||
|
InformationTile {
|
||||||
|
maxWidth: parent.width
|
||||||
|
primaryText: qsTr("Confirmations")
|
||||||
|
secondaryText: {
|
||||||
|
if(root.transaction !== undefined && !!root.transaction )
|
||||||
|
return Math.abs(RootStore.getLatestBlockNumber() - RootStore.hex2Dec(root.transaction.blockNumber))
|
||||||
|
else
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InformationTile {
|
||||||
|
maxWidth: parent.width
|
||||||
|
primaryText: qsTr("Nonce")
|
||||||
|
secondaryText: root.transaction !== undefined && !!root.transaction ? RootStore.hex2Dec(root.transaction.nonce) : ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: fees
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: 8
|
||||||
|
Row {
|
||||||
|
spacing: 8
|
||||||
|
InformationTile {
|
||||||
|
id: baseFee
|
||||||
|
maxWidth: parent.width
|
||||||
|
primaryText: qsTr("Base fee")
|
||||||
|
secondaryText: root.transaction !== undefined && !!root.transaction ? qsTr("%1 Gwei").arg(RootStore.hex2Gwei(root.transaction.baseGasFees)) : ""
|
||||||
|
}
|
||||||
|
InformationTile {
|
||||||
|
maxWidth: parent.width
|
||||||
|
primaryText: qsTr("Tip")
|
||||||
|
secondaryText: root.transaction !== undefined && !!root.transaction ? qsTr("%1 Gwei <font color=\"#939BA1\">• Max: %2 Gwei</font>").
|
||||||
|
arg(RootStore.hex2Gwei(root.transaction.maxPriorityFeePerGas)).
|
||||||
|
arg(RootStore.hex2Gwei(root.transaction.maxFeePerGas)) : ""
|
||||||
|
secondaryLabel.textFormat: Text.RichText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InformationTile {
|
||||||
|
maxWidth: parent.width
|
||||||
|
primaryText: qsTr("Total fee")
|
||||||
|
secondaryText: root.transaction !== undefined && !!root.transaction ? qsTr("%1 Gwei <font color=\"#939BA1\">• Max: %2 Gwei</font>").
|
||||||
|
arg(Utils.stripTrailingZeros(RootStore.hex2Gwei(root.transaction.totalFees))).
|
||||||
|
arg(Utils.stripTrailingZeros(RootStore.hex2Gwei(root.transaction.maxTotalFees))) : ""
|
||||||
|
secondaryLabel.textFormat: Text.RichText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,3 +11,4 @@ ProfileView 1.0 ProfileView.qml
|
||||||
AssetsView 1.0 AssetsView.qml
|
AssetsView 1.0 AssetsView.qml
|
||||||
HistoryView 1.0 HistoryView.qml
|
HistoryView 1.0 HistoryView.qml
|
||||||
AssetsDetailView 1.0 AssetsDetailView.qml
|
AssetsDetailView 1.0 AssetsDetailView.qml
|
||||||
|
TransactionDetailView 1.0 TransactionDetailView.qml
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 65be6f2b96d72161bfa23a384e9d0f2ccfc85c6a
|
Subproject commit 1485b3b4c808dd875d30f17e4be842fcc44c8d35
|
Loading…
Reference in New Issue