refactor: refactor collectibles to be loaded individually

This commit is contained in:
Jonathan Rainville 2020-08-20 17:59:07 -04:00
parent 95f4bd8d09
commit 3219b0f5f4
12 changed files with 226 additions and 202 deletions

View File

@ -10,7 +10,7 @@ QtObject:
WalletView* = ref object of QAbstractListModel
accounts*: AccountList
currentAssetList*: AssetList
currentCollectiblesList*: CollectiblesList
currentCollectiblesLists*: CollectiblesList
currentAccount: AccountItemView
currentTransactions: TransactionList
status: Status
@ -40,7 +40,7 @@ QtObject:
result.currentAccount = newAccountItemView()
result.currentAssetList = newAssetList()
result.currentTransactions = newTransactionList()
result.currentCollectiblesList = newCollectiblesList()
result.currentCollectiblesLists = newCollectiblesList()
result.totalFiatBalance = ""
result.etherscanLink = ""
result.safeLowGasPrice = "0"
@ -79,19 +79,19 @@ QtObject:
proc setCurrentAssetList*(self: WalletView, assetList: seq[Asset])
proc currentCollectiblesListChanged*(self: WalletView) {.signal.}
proc currentCollectiblesListsChanged*(self: WalletView) {.signal.}
proc getCurrentCollectiblesList(self: WalletView): QVariant {.slot.} =
return newQVariant(self.currentCollectiblesList)
proc getCurrentCollectiblesLists(self: WalletView): QVariant {.slot.} =
return newQVariant(self.currentCollectiblesLists)
proc setCurrentCollectiblesList*(self: WalletView, collectibles: seq[Collectible]) =
self.currentCollectiblesList.setNewData(collectibles)
self.currentCollectiblesListChanged()
proc setCurrentCollectiblesLists*(self: WalletView, collectiblesLists: seq[CollectibleList]) =
self.currentCollectiblesLists.setNewData(collectiblesLists)
self.currentCollectiblesListsChanged()
QtProperty[QVariant] collectibles:
read = getCurrentCollectiblesList
write = setCurrentCollectiblesList
notify = currentCollectiblesListChanged
QtProperty[QVariant] collectiblesLists:
read = getCurrentCollectiblesLists
write = setCurrentCollectiblesLists
notify = currentCollectiblesListsChanged
proc currentTransactionsChanged*(self: WalletView) {.signal.}
@ -122,7 +122,8 @@ QtObject:
self.setCurrentAssetList(selectedAccount.assetList)
# Display currently known collectibles, and get latest from API/Contracts
self.setCurrentCollectiblesList(selectedAccount.collectibles)
self.setCurrentCollectiblesLists(selectedAccount.collectiblesLists)
# TODO only load if the list is empty
self.loadCollectiblesForAccount(selectedAccount.address)
# Display currently known transactions, and get latest transactions from status-go
self.setCurrentTransactions(selectedAccount.transactions)
@ -267,26 +268,53 @@ QtObject:
proc addCustomToken*(self: WalletView, address: string, name: string, symbol: string, decimals: string) {.slot.} =
self.status.wallet.toggleAsset(symbol, true, address, name, parseInt(decimals), "")
proc loadingCollectibles*(self: WalletView, isLoading: bool) {.signal.}
proc loadCollectiblesForAccount*(self: WalletView, address: string) {.slot.} =
self.loadingCollectibles(true)
spawnAndSend(self, "setCollectiblesResult ") do:
# Add loading state if it is the current account
if address == self.currentAccount.address:
for collectibleType in status_collectibles.COLLECTIBLE_TYPES:
self.currentCollectiblesLists.addCollectibleListToList(CollectibleList(
collectibleType: collectibleType,
collectiblesJSON: "[]",
error: "",
loading: 1
))
# TODO find a way to use a loop to streamline this code
# Spawn for each collectible. They can end in whichever order
spawnAndSend(self, "setCollectiblesResult") do:
$(%*{
"address": address,
"collectibles": status_collectibles.getAllCollectibles(address)
"collectibleType": status_collectibles.CRYPTOKITTY,
"collectibles": status_collectibles.getCryptoKitties(address)
})
spawnAndSend(self, "setCollectiblesResult") do:
$(%*{
"address": address,
"collectibleType": status_collectibles.KUDO,
"collectibles": status_collectibles.getKudos(address)
})
spawnAndSend(self, "setCollectiblesResult") do:
$(%*{
"address": address,
"collectibleType": status_collectibles.ETHERMON,
"collectibles": status_collectibles.getEthermons(address)
})
proc setCollectiblesResult(self: WalletView, collectiblesJSON: string) {.slot.} =
let collectibleData = parseJson(collectiblesJSON)
let collectibles = collectibleData["collectibles"].to(seq[Collectible]);
let address = collectibleData["address"].getStr
let index = self.accounts.getAccountindexByAddress(address)
if index == -1: return
self.accounts.getAccount(index).collectibles = collectibles
# TODO Add the collectibleData to the Wallet account
# let index = self.accounts.getAccountindexByAddress(address)
# if index == -1: return
# self.accounts.getAccount(index).collectiblesLists = collectiblesList
if address == self.currentAccount.address:
self.setCurrentCollectiblesList(collectibles)
self.loadingCollectibles(false)
# Add CollectibleListJSON to the right list
# TODO check if instead we need to set an error
self.currentCollectiblesLists.setCollectiblesJSONByType(
collectibleData["collectibleType"].getStr,
$collectibleData["collectibles"]
)
proc loadingTrxHistory*(self: WalletView, isLoading: bool) {.signal.}

View File

@ -1,66 +1,68 @@
import NimQml, tables
from ../../../status/wallet import Collectible
from ../../../status/wallet import CollectibleList
type
CollectiblesRoles {.pure.} = enum
Name = UserRole + 1,
Image = UserRole + 2
CollectibleId = UserRole + 3
CollectibleType = UserRole + 4
Description = UserRole + 5
ExternalUrl = UserRole + 6
CollectibleType = UserRole + 1
CollectiblesJSON = UserRole + 2
Error = UserRole + 3
Loading = UserRole + 4
QtObject:
type CollectiblesList* = ref object of QAbstractListModel
collectibles*: seq[Collectible]
collectibleLists*: seq[CollectibleList]
proc setup(self: CollectiblesList) = self.QAbstractListModel.setup
proc delete(self: CollectiblesList) =
self.QAbstractListModel.delete
self.collectibles = @[]
proc newCollectiblesList*(): CollectiblesList =
new(result, delete)
result.collectibles = @[]
result.setup
method rowCount(self: CollectiblesList, index: QModelIndex = nil): int =
return self.collectibles.len
method data(self: CollectiblesList, index: QModelIndex, role: int): QVariant =
if not index.isValid:
return
if index.row < 0 or index.row >= self.collectibles.len:
return
let collectible = self.collectibles[index.row]
let collectibleRole = role.CollectiblesRoles
case collectibleRole:
of CollectiblesRoles.Name: result = newQVariant(collectible.name)
of CollectiblesRoles.Image: result = newQVariant(collectible.image)
of CollectiblesRoles.CollectibleId: result = newQVariant(collectible.id)
of CollectiblesRoles.CollectibleType: result = newQVariant(collectible.collectibleType)
of CollectiblesRoles.Description: result = newQVariant(collectible.description)
of CollectiblesRoles.ExternalUrl: result = newQVariant(collectible.externalUrl)
method roleNames(self: CollectiblesList): Table[int, string] =
{ CollectiblesRoles.Name.int:"name",
CollectiblesRoles.Image.int:"image",
CollectiblesRoles.CollectibleId.int:"collectibleId",
CollectiblesRoles.CollectibleType.int:"collectibleType",
CollectiblesRoles.Description.int:"description",
CollectiblesRoles.ExternalUrl.int:"externalUrl" }.toTable
proc addCollectibleToList*(self: CollectiblesList, colelctible: Collectible) =
self.beginInsertRows(newQModelIndex(), self.collectibles.len, self.collectibles.len)
self.collectibles.add(colelctible)
self.endInsertRows()
proc setNewData*(self: CollectiblesList, collectibles: seq[Collectible]) =
self.beginResetModel()
self.collectibles = collectibles
self.endResetModel()
proc forceUpdate*(self: CollectiblesList) =
self.beginResetModel()
self.endResetModel()
proc delete(self: CollectiblesList) =
self.QAbstractListModel.delete
self.collectibleLists = @[]
proc newCollectiblesList*(): CollectiblesList =
new(result, delete)
result.collectibleLists = @[]
result.setup
proc setCollectiblesJSONByType*(self: CollectiblesList, collectibleType: string, collectiblesJSON: string) =
for collectibleList in self.collectibleLists:
if collectibleList.collectibleType == collectibleType:
collectibleList.collectiblesJSON = collectiblesJSON
collectibleList.loading = 0
self.forceUpdate()
break
method rowCount(self: CollectiblesList, index: QModelIndex = nil): int =
return self.collectibleLists.len
method data(self: CollectiblesList, index: QModelIndex, role: int): QVariant =
if not index.isValid:
return
if index.row < 0 or index.row >= self.collectibleLists.len:
return
let collectibleList = self.collectibleLists[index.row]
let collectibleRole = role.CollectiblesRoles
case collectibleRole:
of CollectiblesRoles.CollectibleType: result = newQVariant(collectibleList.collectibleType)
of CollectiblesRoles.CollectiblesJSON: result = newQVariant(collectibleList.collectiblesJSON)
of CollectiblesRoles.Error: result = newQVariant(collectibleList.error)
of CollectiblesRoles.Loading: result = newQVariant(collectibleList.loading)
method roleNames(self: CollectiblesList): Table[int, string] =
{ CollectiblesRoles.CollectibleType.int:"collectibleType",
CollectiblesRoles.CollectiblesJSON.int:"collectiblesJSON",
CollectiblesRoles.Error.int:"error",
CollectiblesRoles.Loading.int:"loading" }.toTable
proc addCollectibleListToList*(self: CollectiblesList, collectibleList: CollectibleList) =
self.beginInsertRows(newQModelIndex(), self.collectibleLists.len, self.collectibleLists.len)
self.collectibleLists.add(collectibleList)
self.endInsertRows()
proc setNewData*(self: CollectiblesList, collectibleLists: seq[CollectibleList]) =
self.beginResetModel()
self.collectibleLists = collectibleLists
self.endResetModel()

View File

@ -186,8 +186,6 @@ proc getTransfersByAddress*(self: WalletModel, address: string): seq[Transaction
proc validateMnemonic*(self: WalletModel, mnemonic: string): string =
result = status_wallet.validateMnemonic(mnemonic).parseJSON()["error"].getStr
proc getAllCollectibles*(self: WalletModel, address: string): seq[Collectible] = getAllCollectibles(address)
proc getGasPricePredictions*(self: WalletModel): GasPricePrediction =
try:
let url: string = fmt"https://etherchain.org/api/gasPriceOracle"

View File

@ -1,6 +1,10 @@
from eventemitter import Args
import ../libstatus/types
type CollectibleList* = ref object
collectibleType*, collectiblesJSON*, error*: string
loading*: int
type Collectible* = ref object
name*, image*, id*, collectibleType*, description*, externalUrl*: string
@ -15,7 +19,7 @@ type WalletAccount* = ref object
realFiatBalance*: float
assetList*: seq[Asset]
wallet*, chat*: bool
collectibles*: seq[Collectible]
collectiblesLists*: seq[CollectibleList]
transactions*: seq[Transaction]
type AccountArgs* = ref object of Args

View File

@ -7,9 +7,11 @@ import eth/common/eth_types
import ../libstatus/types
import account
const CRYPTOKITTY = "cryptokitty"
const KUDO = "kudo"
const ETHERMON = "ethermon"
const CRYPTOKITTY* = "cryptokitty"
const KUDO* = "kudo"
const ETHERMON* = "ethermon"
const COLLECTIBLE_TYPES* = [CRYPTOKITTY, KUDO, ETHERMON]
proc getTokenUri(contract: Contract, tokenId: Stuint[256]): string =
try:
@ -89,6 +91,10 @@ proc getCryptoKitties*(address: EthAddress): seq[Collectible] =
except Exception as e:
error "Error getting Cryptokitties", msg = e.msg
proc getCryptoKitties*(address: string): seq[Collectible] =
let eth_address = parseAddress(address)
result = getCryptoKitties(eth_address)
proc getEthermons*(address: EthAddress): seq[Collectible] =
result = @[]
try:
@ -120,6 +126,10 @@ proc getEthermons*(address: EthAddress): seq[Collectible] =
except Exception as e:
error "Error getting Ethermons", msg = e.msg
proc getEthermons*(address: string): seq[Collectible] =
let eth_address = parseAddress(address)
result = getEthermons(eth_address)
proc getKudos*(address: EthAddress): seq[Collectible] =
result = @[]
try:
@ -152,6 +162,6 @@ proc getKudos*(address: EthAddress): seq[Collectible] =
except Exception as e:
error "Error getting Kudos", msg = e.msg
proc getAllCollectibles*(address: string): seq[Collectible] =
proc getKudos*(address: string): seq[Collectible] =
let eth_address = parseAddress(address)
result = concat(getCryptoKitties(eth_address), getEthermons(eth_address), getKudos(eth_address))
result = getKudos(eth_address)

View File

@ -3,86 +3,61 @@ import QtGraphicalEffects 1.13
import "../../../imports"
import "../../../shared"
import "./components/collectiblesComponents"
import "./components/collectiblesComponents/collectiblesData.js" as CollectiblesData
Item {
property bool isLoading: true
id: root
Loader {
active: true
sourceComponent: root.isLoading || walletModel.collectibles.rowCount() > 0 ? collectiblesListComponent
: noCollectiblesComponent
width: parent.width
StyledText {
id: noCollectiblesText
color: Style.current.secondaryText
text: qsTr("Collectibles will appear here")
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: 15
}
Component {
id: noCollectiblesComponent
StyledText {
color: Style.current.secondaryText
text: qsTr("Collectibles will appear here")
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: 15
visible: !root.isLoading && walletModel.collectibles.rowCount() === 0
}
}
CollectiblesModal {
id: collectiblesModalComponent
}
Component {
id: collectiblesListComponent
Column {
spacing: Style.current.halfPadding
anchors.fill: parent
CollectiblesContainer {
collectibleName: "CryptoKitties"
collectibleType: Constants.cryptokitty
collectibleIconSource: "../../img/collectibles/CryptoKitties.png"
isLoading: root.isLoading
collectiblesModal: collectiblesModalComponent
buttonText: qsTr("View in Cryptokitties")
getLink: function (id) {
return `https://www.cryptokitties.co/kitty/${id}`
}
}
CollectiblesContainer {
collectibleName: "Ethermons"
collectibleType: Constants.ethermon
collectibleIconSource: "../../img/collectibles/ethermons.png"
isLoading: root.isLoading
collectiblesModal: collectiblesModalComponent
buttonText: qsTr("View in Ethermon")
getLink: function (id) {
// TODO find a more direct URL
return "https://ethermon.io/inventory"
}
}
CollectiblesContainer {
collectibleName: "Kudos"
collectibleType: Constants.kudo
collectibleIconSource: "../../img/collectibles/kudos.png"
isLoading: root.isLoading
collectiblesModal: collectiblesModalComponent
buttonText: qsTr("View in Gitcoin")
getLink: function (id, externalUrl) {
return externalUrl
}
function checkCollectiblesVisibility() {
// Show the collectibles section only if at least one of the sub-items is visible
// Sub-items are visible only if they are loading or have more than zero collectible
let showCollectibles = false
let currentItem
for (let i = 0; i < collectiblesRepeater.count; i++) {
currentItem = collectiblesRepeater.itemAt(i)
if (currentItem && currentItem.active) {
showCollectibles = true
break
}
}
noCollectiblesText.visible = !showCollectibles
collectiblesSection.visible = showCollectibles
}
Connections {
target: walletModel
onLoadingCollectibles: {
root.isLoading= isLoading
Column {
id: collectiblesSection
spacing: Style.current.halfPadding
anchors.fill: parent
Repeater {
id: collectiblesRepeater
model: walletModel.collectiblesLists
CollectiblesContainer {
property var collectibleData: CollectiblesData.collectiblesData[model.collectibleType]
collectibleName: collectibleData.collectibleName
collectibleIconSource: "../../img/collectibles/" + collectibleData.collectibleIconSource
collectiblesModal: collectiblesModalComponent
buttonText: collectibleData.buttonText
getLink: collectibleData.getLink
onVisibleChanged: {
checkCollectiblesVisibility()
}
}
}
}
}

View File

@ -80,9 +80,6 @@ SplitView {
anchors.leftMargin: 32
//% "Collectibles"
btnText: qsTrId("wallet-collectibles")
onClicked: {
walletModel.loadCollectiblesForAccount(walletModel.currentAccount.address)
}
}
StatusTabButton {
id: historyBtn

View File

@ -7,14 +7,25 @@ Item {
property url collectibleIconSource: "../../../../img/collectibles/CryptoKitties.png"
property string collectibleName: "CryptoKitties"
property string collectibleType: "cryptokitty"
property bool isLoading: true
property bool collectiblesOpened: false
property var collectiblesModal
property string buttonText: "View in Cryptokitties"
property var getLink: function () {}
property var collectibles: {
try {
return JSON.parse(collectiblesJSON)
} catch (e) {
console.error('Error parsing collectibles for:', collectibleName)
console.error('JSON:', collectiblesJSON)
console.error('Error:', e)
return []
}
}
// Adding active instead of just using visible, because visible counts as false when the parent is not visible
property bool active: !!loading || collectibles.length > 0
id: root
visible: isLoading || collectiblesContent.collectiblesQty > 0
visible: active
width: parent.width
height: visible ? collectiblesHeader.height + collectiblesContent.height : 0
@ -22,8 +33,8 @@ Item {
id: collectiblesHeader
collectibleName: root.collectibleName
collectibleIconSource: root.collectibleIconSource
collectiblesQty: collectiblesContent.collectiblesQty
isLoading: root.isLoading
collectiblesQty: collectibles.length
isLoading: loading
toggleCollectible: function () {
root.collectiblesOpened = !root.collectiblesOpened
}
@ -38,6 +49,7 @@ Item {
getLink: root.getLink
anchors.top: collectiblesHeader.bottom
anchors.topMargin: Style.current.halfPadding
collectibles: root.collectibles
}
}

View File

@ -11,7 +11,7 @@ ScrollView {
property var collectiblesModal
property string buttonText: "View in Cryptokitties"
property var getLink: function () {}
property alias collectiblesQty: collectibleModel.count
property var collectibles: []
id: root
height: visible ? contentRow.height : 0
@ -26,40 +26,9 @@ ScrollView {
spacing: Style.current.padding
Repeater {
model: collectibleModel
}
model: collectibles
DelegateModel {
id: collectibleModel
model: walletModel.collectibles
items.includeByDefault: false
groups: [
DelegateModelGroup {
id: uncheckedItems
name: "unchecked"
includeByDefault: true
onChanged: {
while (uncheckedItems.count > 0) {
var currentItem = uncheckedItems.get(0)
if (currentItem.model.collectibleType === root.collectibleType) {
currentItem.groups = "items"
} else {
currentItem.groups = "bad"
}
}
}
},
DelegateModelGroup {
id: badCollectibleGroup
name: "bad"
includeByDefault: true
}
]
delegate: Rectangle {
Rectangle {
radius: 16
border.width: 1
border.color: Style.current.border
@ -71,7 +40,7 @@ ScrollView {
id: collectibleImage
width: root.imageSize
height: root.imageSize
source: image
source: modelData.image
fillMode: Image.PreserveAspectCrop
}
@ -80,12 +49,12 @@ ScrollView {
anchors.fill: parent
onClicked: {
collectiblesModal.openModal({
name: name,
id: collectibleId,
description: description,
name: modelData.name,
id: modelData.id,
description: modelData.description,
buttonText: root.buttonText,
link: root.getLink(collectibleId, externalUrl),
image: image
link: root.getLink(modelData.id, modelData.externalUrl),
image: modelData.image
})
}
}

View File

@ -0,0 +1,32 @@
var cryptokitty = "cryptokitty"
var kudo = "kudo"
var ethermon = "ethermon"
var collectiblesData = {
[cryptokitty] :{
collectibleName: "CryptoKitties",
collectibleIconSource: "CryptoKitties.png",
buttonText: qsTr("View in Cryptokitties"),
getLink: function (id) {
return `https://www.cryptokitties.co/kitty/${id}`
}
},
[ethermon] :{
collectibleName: "Ethermons",
collectibleIconSource: "ethermons.png",
buttonText: qsTr("View in Ethermon"),
getLink: function (id) {
// TODO find a more direct URL
return "https://ethermon.io/inventory"
}
},
[kudo] :{
collectibleName: "Kudos",
collectibleIconSource: "kudos.png",
buttonText: qsTr("View in Gitcoin"),
getLink: function (id, externalUrl) {
return externalUrl
}
},
}

View File

@ -23,10 +23,6 @@ QtObject {
readonly property string seedWalletType: "seed"
readonly property string generatedWalletType: "generated"
readonly property string cryptokitty: "cryptokitty"
readonly property string kudo: "kudo"
readonly property string ethermon: "ethermon"
readonly property var accountColors: [
"#9B832F",
"#D37EF4",

View File

@ -157,6 +157,7 @@ DISTFILES += \
app/AppLayouts/Wallet/components/collectiblesComponents/CollectiblesHeader.qml \
app/AppLayouts/Wallet/components/collectiblesComponents/CollectiblesModal.qml \
app/AppLayouts/Wallet/components/collectiblesComponents/CollectiblesModalContent.qml \
app/AppLayouts/Wallet/components/collectiblesComponents/collectiblesData.js \
fonts/InterStatus/InterStatus-Black.otf \
fonts/InterStatus/InterStatus-BlackItalic.otf \
fonts/InterStatus/InterStatus-Bold.otf \