feat(@desktop/wallet): Update token details view

closes #12373
This commit is contained in:
Khushboo Mehta 2024-02-20 10:04:39 +01:00 committed by Khushboo-dev-cpp
parent aed61b68b1
commit 085bf762a5
23 changed files with 641 additions and 474 deletions

View File

@ -209,7 +209,8 @@ proc newModule*[T](
result, events, tokenService, collectibleService, currencyService,
transactionService, walletAccountService,
settingsService, savedAddressService, networkService, accountsService,
keycardService, nodeService, networkConnectionService, devicesService
keycardService, nodeService, networkConnectionService, devicesService,
communityTokensService
)
result.browserSectionModule = browser_section_module.newModule(
result, events, bookmarkService, settingsService, networkService,

View File

@ -8,6 +8,7 @@ import app_service/service/wallet_account/service as wallet_account_service
import app/modules/shared_models/currency_amount
import app_service/service/currency/dto
import app_service/service/settings/service as settings_service
import app_service/service/community_tokens/service as community_tokens_service
type
Controller* = ref object of RootObj
@ -16,6 +17,7 @@ type
tokenService: token_service.Service
walletAccountService: wallet_account_service.Service
settingsService: settings_service.Service
communityTokensService: community_tokens_service.Service
displayAssetsBelowBalanceThreshold: CurrencyAmount
proc newController*(
@ -23,7 +25,8 @@ proc newController*(
events: EventEmitter,
tokenService: token_service.Service,
walletAccountService: wallet_account_service.Service,
settingsService: settings_service.Service
settingsService: settings_service.Service,
communityTokensService: community_tokens_service.Service
): Controller =
result = Controller()
result.events = events
@ -31,6 +34,7 @@ proc newController*(
result.tokenService = tokenService
result.walletAccountService = walletAccountService
result.settingsService = settingsService
result.communityTokensService = communityTokensService
proc delete*(self: Controller) =
discard
@ -85,6 +89,12 @@ proc getTokensDetailsLoading*(self: Controller): bool =
proc getTokensMarketValuesLoading*(self: Controller): bool =
self.tokenService.getTokensMarketValuesLoading()
proc getCommunityTokenDescription*(self: Controller, addressPerChain: seq[AddressPerChain]): string =
self.communityTokensService.getCommunityTokenDescription(addressPerChain)
proc getCommunityTokenDescription*(self: Controller, chainId: int, address: string): string =
self.communityTokensService.getCommunityTokenDescription(chainId, address)
proc updateTokenPreferences*(self: Controller, tokenPreferencesJson: string) =
self.tokenService.updateTokenPreferences(tokenPreferencesJson)

View File

@ -114,9 +114,11 @@ QtObject:
of ModelRole.CommunityId:
result = newQVariant(item.communityId)
of ModelRole.Description:
let tokenDetails = self.delegate.getTokenDetails(item.symbol)
result = if not tokenDetails.isNil: newQVariant(tokenDetails.description)
else: newQVariant("")
result = if not item.communityId.isEmptyOrWhitespace:
newQVariant(self.delegate.getCommunityTokenDescription(item.chainId, item.address))
else:
if self.delegate.getTokensDetailsLoading(): newQVariant("")
else: newQVariant(self.delegate.getTokenDetails(item.symbol).description)
of ModelRole.WebsiteUrl:
let tokenDetails = self.delegate.getTokenDetails(item.symbol)
result = if not tokenDetails.isNil: newQVariant(tokenDetails.assetWebsiteUrl)

View File

@ -10,6 +10,7 @@ type
FlatTokenModelDataSource* = tuple[
getFlatTokensList: proc(): var seq[TokenItem],
getTokenDetails: proc(symbol: string): TokenDetailsItem,
getCommunityTokenDescription: proc(chainId: int, address: string): string,
getTokensDetailsLoading: proc(): bool,
getTokensMarketValuesLoading: proc(): bool,
]
@ -17,6 +18,7 @@ type
TokenBySymbolModelDataSource* = tuple[
getTokenBySymbolList: proc(): var seq[TokenBySymbolItem],
getTokenDetails: proc(symbol: string): TokenDetailsItem,
getCommunityTokenDescription: proc(addressPerChain: seq[AddressPerChain]): string,
getTokensDetailsLoading: proc(): bool,
getTokensMarketValuesLoading: proc(): bool,
]

View File

@ -11,6 +11,7 @@ import app_service/service/wallet_account/service as wallet_account_service
import app_service/service/token/dto
import app_service/service/currency/service
import app_service/service/settings/service as settings_service
import app_service/service/community_tokens/service as community_tokens_service
export io_interface
@ -28,13 +29,14 @@ proc newModule*(
events: EventEmitter,
tokenService: token_service.Service,
walletAccountService: wallet_account_service.Service,
settingsService: settings_service.Service
settingsService: settings_service.Service,
communityTokensService: community_tokens_service.Service
): Module =
result = Module()
result.delegate = delegate
result.events = events
result.view = newView(result)
result.controller = controller.newController(result, events, tokenService, walletAccountService, settingsService)
result.controller = controller.newController(result, events, tokenService, walletAccountService, settingsService, communityTokensService)
result.moduleLoaded = false
result.addresses = @[]
@ -67,6 +69,8 @@ method load*(self: Module) =
self.events.on(SIGNAL_TOKEN_PREFERENCES_UPDATED) do(e: Args):
let args = ResultArgs(e)
self.view.tokenPreferencesUpdated(args.success)
self.events.on(SIGNAL_COMMUNITY_TOKENS_DETAILS_LOADED) do(e: Args):
self.view.tokensDetailsUpdated()
self.events.on(SIGNAL_CURRENCY_FORMATS_UPDATED) do(e:Args):
self.view.currencyFormatsUpdated()
@ -109,6 +113,7 @@ method getFlatTokenModelDataSource*(self: Module): FlatTokenModelDataSource =
return (
getFlatTokensList: proc(): var seq[TokenItem] = self.controller.getFlatTokensList(),
getTokenDetails: proc(symbol: string): TokenDetailsItem = self.controller.getTokenDetails(symbol),
getCommunityTokenDescription: proc(chainId: int, address: string): string = self.controller.getCommunityTokenDescription(chainId, address),
getTokensDetailsLoading: proc(): bool = self.controller.getTokensDetailsLoading(),
getTokensMarketValuesLoading: proc(): bool = self.controller.getTokensMarketValuesLoading()
)
@ -117,6 +122,7 @@ method getTokenBySymbolModelDataSource*(self: Module): TokenBySymbolModelDataSou
return (
getTokenBySymbolList: proc(): var seq[TokenBySymbolItem] = self.controller.getTokenBySymbolList(),
getTokenDetails: proc(symbol: string): TokenDetailsItem = self.controller.getTokenDetails(symbol),
getCommunityTokenDescription: proc(addressPerChain: seq[AddressPerChain]): string = self.controller.getCommunityTokenDescription(addressPerChain),
getTokensDetailsLoading: proc(): bool = self.controller.getTokensDetailsLoading(),
getTokensMarketValuesLoading: proc(): bool = self.controller.getTokensMarketValuesLoading()
)

View File

@ -116,7 +116,10 @@ QtObject:
of ModelRole.CommunityId:
result = newQVariant(item.communityId)
of ModelRole.Description:
result = if not item.communityId.isEmptyOrWhitespace or self.delegate.getTokensDetailsLoading() : newQVariant("")
result = if not item.communityId.isEmptyOrWhitespace:
newQVariant(self.delegate.getCommunityTokenDescription(item.addressPerChainId))
else:
if self.delegate.getTokensDetailsLoading() : newQVariant("")
else: newQVariant(self.delegate.getTokenDetails(item.symbol).description)
of ModelRole.WebsiteUrl:
result = if not item.communityId.isEmptyOrWhitespace or self.delegate.getTokensDetailsLoading() : newQVariant("")

View File

@ -37,6 +37,7 @@ import app_service/service/accounts/service as accounts_service
import app_service/service/node/service as node_service
import app_service/service/network_connection/service as network_connection_service
import app_service/service/devices/service as devices_service
import app_service/service/community_tokens/service as community_tokens_service
import backend/collectibles as backend_collectibles
import backend/activity as backend_activity
@ -107,7 +108,8 @@ proc newModule*(
keycardService: keycard_service.Service,
nodeService: node_service.Service,
networkConnectionService: network_connection_service.Service,
devicesService: devices_service.Service
devicesService: devices_service.Service,
communityTokensService: community_tokens_service.Service
): Module =
result = Module()
result.delegate = delegate
@ -121,7 +123,7 @@ proc newModule*(
result.controller = newController(result, settingsService, walletAccountService, currencyService, networkService)
result.accountsModule = accounts_module.newModule(result, events, walletAccountService, networkService, currencyService)
result.allTokensModule = all_tokens_module.newModule(result, events, tokenService, walletAccountService, settingsService)
result.allTokensModule = all_tokens_module.newModule(result, events, tokenService, walletAccountService, settingsService, communityTokensService)
let allCollectiblesModule = all_collectibles_module.newModule(result, events, collectibleService, networkService, walletAccountService, settingsService)
result.allCollectiblesModule = allCollectiblesModule
result.assetsModule = assets_module.newModule(result, events, walletAccountService, networkService, tokenService,

View File

@ -843,6 +843,20 @@ QtObject:
if token.chainId == chainId and token.address == address:
return token
proc getCommunityTokenDescription*(self: Service, chainId: int, address: string): string =
let communityTokens = self.getAllCommunityTokens()
for token in communityTokens:
if token.chainId == chainId and cmpIgnoreCase(token.address, address) == 0:
return token.description
return ""
proc getCommunityTokenDescription*(self: Service, addressPerChain: seq[AddressPerChain]): string =
for apC in addressPerChain:
let description = self.getCommunityTokenDescription(apC.chainId, apC.address)
if not description.isEmptyOrWhitespace:
return description
return ""
proc getCommunityTokenBurnState*(self: Service, chainId: int, contractAddress: string): ContractTransactionStatus =
let burnTransactions = self.transactionService.getPendingTransactionsForType(PendingTransactionTypeDto.BurnCommunityToken)
for transaction in burnTransactions:

View File

@ -131,6 +131,13 @@ SplitView {
onManageTokensRequested: logs.logEvent("onManageTokensRequested")
}
ColumnLayout {
Layout.fillHeight: true
Layout.fillWidth: true
Button {
text: "go back"
onClicked: stack.currentIndex = 0
}
AssetsDetailView {
id: detailsView
Layout.fillHeight: true
@ -138,17 +145,12 @@ SplitView {
currencyStore: d.currencyStore
allNetworksModel: NetworksModel.allNetworks
networkFilters: d.networksChainsCurrentlySelected
Button {
anchors.top: parent.top
text: "go back"
onClicked: stack.currentIndex = 0
}
}
}
Pane {
SplitView.minimumWidth: 300
SplitView.preferredWidth: 300
SplitView.preferredWidth: 250
ColumnLayout {
spacing: 12

View File

@ -107,7 +107,7 @@ ListModel {
decimals: 0,
type: 1,
communityId: "ddls",
description: "",
description: "It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout." ,
websiteUrl: "",
marketDetails: {
marketCap: ({amount: 0, symbol: "USD", displayDecimals: 2, stripTrailingZeroes: false}),
@ -159,7 +159,7 @@ ListModel {
decimals: 0,
type: 1,
communityId: "ddls",
description: "",
description: "It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. ",
websiteUrl: "",
marketDetails: {
marketCap: ({amount: 0, symbol: "USD", displayDecimals: 2, stripTrailingZeroes: false}),

View File

@ -122,7 +122,7 @@ Page {
property bool isTimeRange: false
leftPadding: 0
width: implicitWidth
width: visible ? implicitWidth: 0
onClicked: {
root.headerTabClicked(privateIdentifier, isTimeRange);
}
@ -144,6 +144,7 @@ Page {
for (var j = 0; j < graphsModel.length; j++) {
var graphTab = tabButton.createObject(root, { text: graphsModel[j].text,
enabled: graphsModel[j].enabled,
visible: graphsModel[j].visible,
isTimeRange: false,
privateIdentifier: typeof graphsModel[j].id !== "undefined" ? graphsModel[j].id : null});
graphsTabBar.addItem(graphTab);

View File

@ -284,6 +284,10 @@ QtObject {
}
return ""
}
function stripHttpsAndwwwFromUrl(text) {
return text.replace(/http(s)?(:)?(\/\/)?|(\/\/)?(www\.)?(\/)/gim, '')
}
}

View File

@ -171,7 +171,7 @@ StatusScrollView {
regexValidator.regularExpression: Constants.regularExpressions.ascii
regexValidator.errorMessage: qsTr("Only A-Z, 0-9 and standard punctuation allowed")
onTextChanged: root.token.description
onTextChanged: root.token.description = text
}
CustomStatusInput {

View File

@ -13,7 +13,7 @@ InformationTag {
tagPrimaryLabel.color: Theme.palette.directColor1
tagSecondaryLabel.color: Theme.palette.directColor1
middleLabel.color: Theme.palette.baseColor1
iconAsset.color: Theme.palette.primaryColor1
asset.color: Theme.palette.primaryColor1
secondarylabelMaxWidth: 1000
height: 32
customBackground: Component {

View File

@ -88,7 +88,8 @@ ColumnLayout {
InformationTag {
id: networkTag
readonly property bool isNetworkValid: networkShortName !== ""
image.source: isNetworkValid && networkIconURL !== "" ? Style.svg("tiny/" + networkIconURL) : ""
asset.name: isNetworkValid && networkIconURL !== "" ? Style.svg("tiny/" + networkIconURL) : ""
asset.isImage: true
tagPrimaryLabel.text: isNetworkValid ? networkShortName : "---"
tagPrimaryLabel.color: isNetworkValid ? networkColor : "black"
visible: isNetworkValid

View File

@ -0,0 +1,42 @@
import QtQuick 2.13
import QtQuick.Layouts 1.13
import QtQuick.Controls 2.14
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import utils 1.0
Control {
id: root
property alias primaryText: primaryText.text
property alias primaryLabel: primaryText
property alias content: content.sourceComponent
padding: 12
background: Rectangle {
radius: Style.current.radius
border.width: 1
border.color: Theme.palette.baseColor2
color: Style.current.transparent
}
contentItem: ColumnLayout {
spacing: 4
StatusBaseText {
id: primaryText
Layout.fillWidth: true
font.pixelSize: 13
lineHeight: 18
lineHeightMode: Text.FixedHeight
color: Theme.palette.directColor5
visible: text
elide: Text.ElideRight
}
Loader {
id: content
}
}
}

View File

@ -10,3 +10,4 @@ ManageTokenMenuButton 1.0 ManageTokenMenuButton.qml
ManageTokensCommunityTag 1.0 ManageTokensCommunityTag.qml
ManageTokensDelegate 1.0 ManageTokensDelegate.qml
ManageTokensGroupDelegate 1.0 ManageTokensGroupDelegate.qml
InformationTileAssetDetails 1.0 InformationTileAssetDetails.qml

View File

@ -62,7 +62,7 @@ Column {
return qsTr("to")
}
}
iconAsset.icon: "history"
asset.name: "history"
visible: activityFilterMenu.selectedTime !== Constants.TransactionTimePeriod.All
onClosed: activityFilterStore.setSelectedTimestamp(Constants.TransactionTimePeriod.All)
}
@ -88,7 +88,7 @@ Column {
console.warn("Unhandled type :: ",activityFilterStore.typeFilters[index])
return ""
}
iconAsset.icon: switch(activityFilterStore.typeFilters[index]) {
asset.name: switch(activityFilterStore.typeFilters[index]) {
case Constants.TransactionType.Send:
return "send"
case Constants.TransactionType.Receive:
@ -126,7 +126,7 @@ Column {
console.warn("Unhandled status :: ",activityFilterStore.statusFilters[index])
return ""
}
iconAsset.icon: switch(activityFilterStore.statusFilters[index]) {
asset.name: switch(activityFilterStore.statusFilters[index]) {
case Constants.TransactionStatus.Failed:
return Style.svg("transaction/failed")
case Constants.TransactionStatus.Pending:
@ -139,7 +139,7 @@ Column {
console.warn("Unhandled status :: ",activityFilterStore.statusFilters[index])
return ""
}
iconAsset.color: "transparent"
asset.color: "transparent"
onClosed: activityFilterStore.toggleStatus(status, activityFilterMenu.allStatusChecked)
}
}
@ -148,8 +148,8 @@ Column {
model: activityFilterStore.tokensFilter
delegate: ActivityFilterTagItem {
tagPrimaryLabel.text: modelData
iconAsset.icon: Constants.tokenIcon(modelData)
iconAsset.color: "transparent"
asset.name: Constants.tokenIcon(modelData)
asset.color: "transparent"
onClosed: activityFilterStore.toggleToken(modelData)
}
}
@ -170,8 +170,8 @@ Column {
return "#" + data[2]
return ""
}
iconAsset.icon: activityFilterStore.collectiblesList.getImageUrl(uid)
iconAsset.color: "transparent"
asset.name: activityFilterStore.collectiblesList.getImageUrl(uid)
asset.color: "transparent"
onClosed: activityFilterStore.toggleCollectibles(uid)
Connections {

View File

@ -244,7 +244,8 @@ StatusModal {
delegate: InformationTag {
tagPrimaryLabel.text: model.shortName
tagPrimaryLabel.color: model.chainColor
image.source: Style.svg("tiny/" + model.iconUrl)
asset.name: Style.svg("tiny/" + model.iconUrl)
asset.isImage: true
visible: d.preferredChainIdsArray.includes(model.chainId.toString())
}
}

View File

@ -17,6 +17,8 @@ import shared.stores 1.0
import SortFilterProxyModel 0.2
import "../controls"
/// \beware: heavy shortcuts here, refactor to match the requirements when touching this again
/// \todo split into token history and balance views; they have different requirements that introduce unnecessary complexity
/// \todo take a declarative approach, move logic into the typed backend and remove multiple source of truth (e.g. time ranges)
@ -38,6 +40,7 @@ Item {
readonly property string symbol: !!root.token? root.token.symbol?? "" : ""
property bool marketDetailsLoading: !!root.token? root.token.marketDetailsLoading?? false : false
property bool tokenDetailsLoading: !!root.token? root.token.detailsLoading?? false: false
property bool isCommunityAsset: !!root.token && token.isCommunityAsset !== undefined ? token.isCommunityAsset : false
readonly property LeftJoinModel addressPerChainModel: LeftJoinModel {
leftModel: token && token.addressPerChain ? token.addressPerChain: null
@ -66,23 +69,38 @@ Item {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
width: parent.width
asset.name: token && token.symbol ? Style.png("tokens/%1".arg(token.symbol)) : ""
asset.name: {
if (!token)
return ""
if (token.image)
return token.image
if (token.symbol)
return Style.png("tokens/%1".arg(token.symbol))
return ""
}
asset.isImage: true
primaryText: token && token.name ? token.name : Constants.dummyText
secondaryText: token ? LocaleUtils.currencyAmountToLocaleString(root.currencyStore.getCurrencyAmount(token.currentBalance, token.symbol)) : Constants.dummyText
tertiaryText: {
if (!d.isCommunityAsset) {
let totalCurrencyBalance = token && token.currentCurrencyBalance && token.symbol ? token.currentCurrencyBalance : 0
return currencyStore.formatCurrencyAmount(totalCurrencyBalance, token.symbol)
return currencyStore.formatCurrencyAmount(totalCurrencyBalance, currencyStore.currentCurrency)
}
return ""
}
decimals: token && token.decimals ? token.decimals : 4
balances: token && token.balances ? token.balances: null
allNetworksModel: root.allNetworksModel
isLoading: d.marketDetailsLoading
address: root.address
errorTooltipText: token && token.balances ? networkConnectionStore.getBlockchainNetworkDownTextForToken(token.balances): ""
formatBalance: function(balance){
return LocaleUtils.currencyAmountToLocaleString(currencyStore.getCurrencyAmount(balance, token.symbol))
}
communityTag.visible: d.isCommunityAsset
communityTag.tagPrimaryLabel.text: d.isCommunityAsset ? token.communityName: ""
communityTag.asset.name: d.isCommunityAsset ? token && !!token.communityImage ? token.communityImage : "" : ""
communityTag.asset.isImage: true
}
enum GraphType {
@ -90,12 +108,24 @@ Item {
Balance
}
StatusScrollView {
id: scrollView
anchors.top: tokenDetailsHeader.bottom
anchors.bottom: parent.bottom
anchors.topMargin: 47
width: parent.width
contentWidth: availableWidth
padding: 0
ColumnLayout {
width: scrollView.availableWidth
spacing: 40
Loader {
id: graphDetailLoader
width: parent.width
height: 290
anchors.top: tokenDetailsHeader.bottom
anchors.topMargin: 24
Layout.fillWidth: true
Layout.preferredHeight: 290
active: root.visible
sourceComponent: StatusChartPanel {
id: graphDetail
@ -127,8 +157,8 @@ Item {
}
graphsModel: [
{text: qsTr("Price"), enabled: true, id: AssetsDetailView.GraphType.Price},
{text: qsTr("Balance"), enabled: true, id: AssetsDetailView.GraphType.Balance},
{text: qsTr("Price"), enabled: true, id: AssetsDetailView.GraphType.Price, visible: !d.isCommunityAsset},
{text: qsTr("Balance"), enabled: true, id: AssetsDetailView.GraphType.Balance, visible: true},
]
defaultTimeRangeIndexShown: ChartStoreBase.TimeRange.All
timeRangeModel: dataReady() && selectedStore.timeRangeTabsModel
@ -293,7 +323,7 @@ Item {
Connections {
target: balanceStore
function onNewDataReady(address, tokenSymbol, currencySymbol, timeRange) {
if (timeRange === timeRangeStrToEnum(graphDetail.selectedTimeRange)) {
if (timeRange === balanceStore.timeRangeStrToEnum(graphDetail.selectedTimeRange)) {
chart.updateToNewData()
}
}
@ -311,19 +341,9 @@ Item {
}
}
ColumnLayout {
anchors.top: graphDetailLoader.bottom
anchors.topMargin: 24
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
width: parent.width
spacing: Style.current.padding
RowLayout {
Layout.fillWidth: true
visible: !d.isCommunityAsset
InformationTile {
maxWidth: parent.width
primaryText: qsTr("Market Cap")
@ -377,58 +397,52 @@ Item {
}
}
StatusTabBar {
Layout.fillWidth: true
Layout.topMargin: Style.current.xlPadding
Flow {
id: detailsFlow
readonly property bool isOverflowing: detailsFlow.width - websiteBlock.width - tokenDescriptionText.width < 24
Layout.fillWidth: true
spacing: 24
StatusTabBar {
width: parent.width
StatusTabButton {
leftPadding: 0
width: implicitWidth
text: qsTr("Overview")
}
visible: tokenDescriptionText.visible
}
StackLayout {
id: stack
Layout.fillWidth: true
Layout.fillHeight: true
StatusScrollView {
id: scrollView
Layout.preferredWidth: parent.width
Layout.preferredHeight: parent.height
topPadding: 8
bottomPadding: 8
contentWidth: availableWidth
Flow {
id: detailsFlow
readonly property bool isOverflowing: detailsFlow.width - tagsLayout.width - tokenDescriptionText.width < 24
spacing: 24
width: scrollView.availableWidth
StatusTextWithLoadingState {
id: tokenDescriptionText
width: Math.max(536 , scrollView.availableWidth - tagsLayout.width - 24)
width: Math.max(536 , scrollView.availableWidth - websiteBlock.width - 24)
font.pixelSize: 15
lineHeight: 22
lineHeightMode: Text.FixedHeight
text: token && token.description ? token.description : Constants.dummyText
text: token && token.description ? token.description : d.tokenDetailsLoading ? Constants.dummyText: ""
customColor: Theme.palette.directColor1
elide: Text.ElideRight
wrapMode: Text.Wrap
textFormat: Qt.RichText
loading: d.tokenDetailsLoading
visible: !!text
}
ColumnLayout {
id: tagsLayout
spacing: 10
InformationTag {
id: website
Layout.alignment: detailsFlow.isOverflowing ? Qt.AlignLeft : Qt.AlignRight
iconAsset.icon: "browser"
tagPrimaryLabel.text: qsTr("Website")
GridLayout{
columnSpacing: 10
rowSpacing: 10
flow: detailsFlow.isOverflowing ? GridLayout.LeftToRight: GridLayout.TopToBottom
InformationTileAssetDetails {
id: websiteBlock
Layout.preferredWidth: 272
visible: !d.isCommunityAsset
primaryText: qsTr("Website")
content: InformationTag {
asset.name : "browser"
tagPrimaryLabel.text: SQUtils.Utils.stripHttpsAndwwwFromUrl(token.websiteUrl)
visible: typeof token != "undefined" && token && token.websiteUrl !== ""
customBackground: Component {
Rectangle {
@ -444,21 +458,52 @@ Item {
onClicked: Global.openLink(token.websiteUrl)
}
}
}
/* TODO :: Issue with not being able to see correct balances after switching assets will be fixed under
https://github.com/status-im/status-desktop/issues/12912 */
InformationTileAssetDetails {
Layout.preferredWidth: 272
visible: d.isCommunityAsset
primaryText: qsTr("Minted by")
content: InformationTag {
tagPrimaryLabel.text: token && token.communityName ? token.communityName : ""
asset.name: token && token.communityImage ? token.communityImage : ""
asset.isImage: true
customBackground: Component {
Rectangle {
color: Theme.palette.baseColor2
border.width: 1
border.color: "transparent"
radius: 36
}
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: Global.switchToCommunity(token.communityId)
}
}
}
InformationTileAssetDetails {
Layout.minimumWidth: 272
Layout.preferredWidth: implicitWidth
primaryText: qsTr("Contract")
content: GridLayout {
columnSpacing: 10
rowSpacing: 10
flow: detailsFlow.isOverflowing ? GridLayout.LeftToRight : GridLayout.TopToBottom
Repeater {
Layout.alignment: detailsFlow.isOverflowing ? Qt.AlignLeft : Qt.AlignRight
model: SortFilterProxyModel {
sourceModel: d.addressPerChainModel
filters: ExpressionFilter {
expression: root.networkFilters.split(":").includes(model.chainId+"")
}
}
InformationTag {
image.source: Style.svg("tiny/" + model.iconUrl)
delegate: InformationTag {
asset.name: Style.svg("tiny/" + model.iconUrl)
asset.isImage: true
tagPrimaryLabel.text: model.chainName
tagSecondaryLabel.text: model.address
tagSecondaryLabel.text: SQUtils.Utils.elideText(model.address, 2,4)
customBackground: Component {
Rectangle {
color: Theme.palette.baseColor2
@ -475,3 +520,4 @@ Item {
}
}
}
}

View File

@ -23,10 +23,6 @@ RightTabBaseView {
signal launchShareAddressModal()
headerButton.onClicked: {
root.launchShareAddressModal()
}
function resetView() {
stack.currentIndex = 0
root.currentTabIndex = 0
@ -38,6 +34,11 @@ RightTabBaseView {
stack.currentIndex = 0;
}
headerButton.onClicked: {
root.launchShareAddressModal()
}
header.visible: stack.currentIndex === 0
StackLayout {
id: stack
anchors.fill: parent

View File

@ -1,5 +1,6 @@
import QtQuick 2.13
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import utils 1.0
import shared.controls 1.0
@ -19,84 +20,113 @@ Control {
property alias primaryText: tokenName.text
property alias secondaryText: cryptoBalance.text
property alias tertiaryText: fiatBalance.text
property alias communityTag: communityTag
property var balances
property int decimals
property var allNetworksModel
property bool isLoading: false
property string errorTooltipText
property string address
property StatusAssetSettings asset: StatusAssetSettings {
width: 40
height: 40
width: 25
height: 25
}
property var formatBalance: function(balance){}
topPadding: Style.current.padding
contentItem: Column {
contentItem: ColumnLayout {
id: mainLayout
spacing: 4
Row {
readonly property bool isOverflowing: parent.width - tokenNameAndIcon.width - communityAndBalances.width < 24
RowLayout {
id: tokenNameAndIcon
Layout.fillWidth: true
spacing: 8
StatusSmartIdenticon {
id: identiconLoader
anchors.verticalCenter: parent.verticalCenter
asset: root.asset
loading: root.isLoading
}
StatusTextWithLoadingState {
id: tokenName
width: Math.min(root.width - identiconLoader.width - cryptoBalance.width - fiatBalance.width - 24, implicitWidth)
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: 22
lineHeight: 30
Layout.alignment: Qt.AlignHCenter
Layout.maximumWidth: root.width-root.asset.width-8
font.pixelSize: 28
font.bold: true
lineHeight: 38
lineHeightMode: Text.FixedHeight
elide: Text.ElideRight
customColor: Theme.palette.directColor1
loading: root.isLoading
}
StatusSmartIdenticon {
Layout.preferredWidth: root.asset.width
Layout.alignment: Qt.AlignHCenter
asset: root.asset
loading: root.isLoading
}
}
GridLayout {
Layout.fillWidth: true
rowSpacing: Style.current.halfPadding
columnSpacing: Style.current.halfPadding
flow: mainLayout.isOverflowing ? GridLayout.TopToBottom: GridLayout.LeftToRight
RowLayout {
Layout.fillWidth: true
spacing: Style.current.halfPadding
StatusTextWithLoadingState {
id: cryptoBalance
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: 22
lineHeight: 30
Layout.alignment: Qt.AlignHCenter
font.pixelSize: 28
font.bold: true
lineHeight: 38
lineHeightMode: Text.FixedHeight
customColor: Theme.palette.baseColor1
loading: root.isLoading
}
StatusBaseText {
id: dotSeparator
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: -15
font.pixelSize: 50
color: Theme.palette.baseColor1
text: "."
}
StatusTextWithLoadingState {
id: fiatBalance
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: 22
lineHeight: 30
Layout.alignment: Qt.AlignBottom
Layout.bottomMargin: 2
font.pixelSize: 15
lineHeight: 22
lineHeightMode: Text.FixedHeight
customColor: Theme.palette.baseColor1
loading: root.isLoading
}
}
Row {
spacing: Style.current.smallPadding
anchors.left: parent.left
anchors.leftMargin: identiconLoader.width
Item {
id: filler
Layout.fillWidth: true
}
RowLayout {
id: communityAndBalances
Layout.fillWidth: true
spacing: Style.current.halfPadding
InformationTag {
id: communityTag
}
Repeater {
id: chainRepeater
Layout.alignment: Qt.AlignRight
model: root.allNetworksModel
delegate: InformationTag {
readonly property double aggregatedbalance: balancesAggregator.value/(10 ** root.decimals)
SortFilterProxyModel {
id: filteredBalances
sourceModel: root.balances
filters: ValueFilter {
filters: [
ValueFilter {
roleName: "chainId"
value: model.chainId
},
ValueFilter {
roleName: "account"
value: root.address.toLowerCase()
enabled: !!root.address
}
]
}
SumAggregator {
id: balancesAggregator
@ -105,11 +135,12 @@ Control {
}
tagPrimaryLabel.text: root.formatBalance(aggregatedbalance)
tagPrimaryLabel.color: model.chainColor
image.source: Style.svg("tiny/%1".arg(model.iconUrl))
asset.name: Style.svg("tiny/%1".arg(model.iconUrl))
asset.isImage: true
loading: root.isLoading
visible: balancesAggregator.value > 0
rightComponent: StatusFlatRoundButton {
width: 14
width: visible ? 14 : 0
height: visible ? 14 : 0
icon.width: 14
icon.height: 14
@ -123,3 +154,4 @@ Control {
}
}
}
}

View File

@ -11,11 +11,10 @@ import utils 1.0
Control {
id: root
property alias image: image
property alias iconAsset: iconAsset
property alias tagPrimaryLabel: tagPrimaryLabel
property alias tagSecondaryLabel: tagSecondaryLabel
property alias middleLabel: middleLabel
property alias asset: smartIdenticon.asset
property alias rightComponent: rightComponent.sourceComponent
property bool loading: false
property int secondarylabelMaxWidth: 100
@ -45,18 +44,15 @@ Control {
contentItem: RowLayout {
spacing: root.spacing
visible: !root.loading
// FIXME this could be StatusIcon but it can't load images from an arbitrary URL
Image {
id: image
StatusSmartIdenticon {
id: smartIdenticon
Layout.maximumWidth: visible ? 16 : 0
Layout.maximumHeight: visible ? 16 : 0
visible: !!source
}
StatusIcon {
id: iconAsset
Layout.maximumWidth: visible ? 16 : 0
Layout.maximumHeight: visible ? 16 : 0
visible: !!icon
asset.width: visible ? 16 : 0
asset.height: visible ? 16 : 0
asset.bgHeight: visible ? 16 : 0
asset.bgWidth: visible ? 16 : 0
visible: !!asset.name
}
StatusBaseText {
id: tagPrimaryLabel