mirror of
https://github.com/status-im/status-desktop.git
synced 2025-02-03 18:25:33 +00:00
feat(@desktop/wallet2): implement buy crypto feature
If user wants to buy/sell crypto, popup modal list will be displayed so he may choose which service to use for that action. Fixes: #2120
This commit is contained in:
parent
7973679926
commit
7bbfba7fb3
@ -43,3 +43,7 @@ proc init*(self: WalletController) =
|
||||
self.status.events.on(SignalType.Wallet.event) do(e:Args):
|
||||
var data = WalletSignal(e)
|
||||
debug "TODO: handle wallet signal", signalType=data.eventType
|
||||
|
||||
self.status.events.on("cryptoServicesFetched") do(e: Args):
|
||||
var args = CryptoServicesArg(e)
|
||||
self.view.onCryptoServicesFetched(args.services)
|
@ -3,6 +3,7 @@ import NimQml, chronicles, stint
|
||||
|
||||
import ../../../status/[status, wallet2]
|
||||
import views/[accounts, account_list, collectibles]
|
||||
import views/buy_sell_crypto/[service_controller]
|
||||
|
||||
QtObject:
|
||||
type
|
||||
@ -10,10 +11,12 @@ QtObject:
|
||||
status: Status
|
||||
accountsView: AccountsView
|
||||
collectiblesView: CollectiblesView
|
||||
cryptoServiceController: CryptoServiceController
|
||||
|
||||
proc delete(self: WalletView) =
|
||||
self.accountsView.delete
|
||||
self.collectiblesView.delete
|
||||
self.cryptoServiceController.delete
|
||||
self.QAbstractListModel.delete
|
||||
|
||||
proc setup(self: WalletView) =
|
||||
@ -24,6 +27,7 @@ QtObject:
|
||||
result.status = status
|
||||
result.accountsView = newAccountsView(status)
|
||||
result.collectiblesView = newCollectiblesView(status)
|
||||
result.cryptoServiceController = newCryptoServiceController(status)
|
||||
result.setup
|
||||
|
||||
proc getAccounts(self: WalletView): QVariant {.slot.} =
|
||||
@ -53,3 +57,12 @@ QtObject:
|
||||
# If it's the first account we ever get, use its list as our first lists
|
||||
if (self.accountsView.accounts.rowCount == 1):
|
||||
self.setCurrentAccountByIndex(0)
|
||||
|
||||
proc getCryptoServiceController*(self: WalletView): QVariant {.slot.} =
|
||||
newQVariant(self.cryptoServiceController)
|
||||
|
||||
QtProperty[QVariant] cryptoServiceController:
|
||||
read = getCryptoServiceController
|
||||
|
||||
proc onCryptoServicesFetched*(self: WalletView, jsonNode: JsonNode) =
|
||||
self.cryptoServiceController.onCryptoServicesFetched(jsonNode)
|
@ -0,0 +1,56 @@
|
||||
import NimQml, json, strutils, chronicles
|
||||
|
||||
import service_model, service_item
|
||||
|
||||
import ../../../../../status/[status, wallet2]
|
||||
|
||||
logScope:
|
||||
topics = "app-wallet2-crypto-service"
|
||||
|
||||
QtObject:
|
||||
type CryptoServiceController* = ref object of QObject
|
||||
status: Status
|
||||
cryptoServiceModel: CryptoServiceModel
|
||||
servicesFetched: bool
|
||||
|
||||
proc setup(self: CryptoServiceController) =
|
||||
self.QObject.setup
|
||||
|
||||
proc delete*(self: CryptoServiceController) =
|
||||
self.cryptoServiceModel.delete
|
||||
self.QObject.delete
|
||||
|
||||
proc newCryptoServiceController*(status: Status): CryptoServiceController =
|
||||
new(result, delete)
|
||||
result.status = status
|
||||
result.cryptoServiceModel = newCryptoServiceModel()
|
||||
result.servicesFetched = false
|
||||
result.setup
|
||||
|
||||
proc getCryptoServiceModel(self: CryptoServiceController): QVariant {.slot.} =
|
||||
newQVariant(self.cryptoServiceModel)
|
||||
|
||||
QtProperty[QVariant] cryptoServiceModel:
|
||||
read = getCryptoServiceModel
|
||||
|
||||
proc fetchCryptoServicesFetched*(self:CryptoServiceController) {.signal.}
|
||||
|
||||
proc fetchCryptoServices*(self: CryptoServiceController) {.slot.} =
|
||||
if(not self.servicesFetched):
|
||||
self.status.wallet2.asyncFetchCryptoServices()
|
||||
else:
|
||||
self.fetchCryptoServicesFetched()
|
||||
|
||||
proc onCryptoServicesFetched*(self: CryptoServiceController, jsonNode: JsonNode) =
|
||||
self.servicesFetched = true
|
||||
|
||||
if (jsonNode.kind != JArray):
|
||||
info "received crypto services list is empty"
|
||||
else:
|
||||
var items: seq[CryptoServiceItem] = @[]
|
||||
for itemObject in jsonNode:
|
||||
items.add(initCryptoServiceItem(itemObject))
|
||||
|
||||
self.cryptoServiceModel.set(items)
|
||||
|
||||
self.fetchCryptoServicesFetched()
|
65
src/app/wallet/v2/views/buy_sell_crypto/service_item.nim
Normal file
65
src/app/wallet/v2/views/buy_sell_crypto/service_item.nim
Normal file
@ -0,0 +1,65 @@
|
||||
import json, strformat, chronicles
|
||||
|
||||
include ../../../../../status/utils/[json_utils]
|
||||
|
||||
logScope:
|
||||
topics = "app-wallet2-crypto-service"
|
||||
|
||||
type CryptoServiceItem* = object
|
||||
name: string
|
||||
description: string
|
||||
fees: string
|
||||
logoUrl: string
|
||||
siteUrl: string
|
||||
hostname: string
|
||||
|
||||
proc initCryptoServiceItem*(name, description, fees, logoUrl, siteUrl,
|
||||
hostname: string): CryptoServiceItem =
|
||||
|
||||
result.name = name
|
||||
result.description = description
|
||||
result.fees = fees
|
||||
result.logoUrl = logoUrl
|
||||
result.siteUrl = siteUrl
|
||||
result.hostname = hostname
|
||||
|
||||
proc initCryptoServiceItem*(jsonObject: JsonNode): CryptoServiceItem =
|
||||
|
||||
if (jsonObject.kind != JObject):
|
||||
info "CryptoServiceItem initialization failed: JsonNode is not JObject"
|
||||
return
|
||||
|
||||
discard jsonObject.getProp("name", result.name)
|
||||
discard jsonObject.getProp("description", result.description)
|
||||
discard jsonObject.getProp("fees", result.fees)
|
||||
discard jsonObject.getProp("logoUrl", result.logoUrl)
|
||||
discard jsonObject.getProp("siteUrl", result.siteUrl)
|
||||
discard jsonObject.getProp("hostname", result.hostname)
|
||||
|
||||
proc `$`*(self: CryptoServiceItem): string =
|
||||
result = "CryptoServiceItem("
|
||||
result &= fmt"name:{self.name}, "
|
||||
result &= fmt"description:{self.description}, "
|
||||
result &= fmt"fees:{self.fees}, "
|
||||
result &= fmt"logoUrl:{self.logoUrl}, "
|
||||
result &= fmt"siteUrl:{self.siteUrl}"
|
||||
result &= fmt"hostname:{self.hostname}"
|
||||
result &= ")"
|
||||
|
||||
method getName*(self: CryptoServiceItem): string {.base.} =
|
||||
return self.name
|
||||
|
||||
method getDescription*(self: CryptoServiceItem): string {.base.} =
|
||||
return self.description
|
||||
|
||||
method getFees*(self: CryptoServiceItem): string {.base.} =
|
||||
return self.fees
|
||||
|
||||
method getLogoUrl*(self: CryptoServiceItem): string {.base.} =
|
||||
return self.logoUrl
|
||||
|
||||
method getSiteUrl*(self: CryptoServiceItem): string {.base.} =
|
||||
return self.siteUrl
|
||||
|
||||
method getHostname*(self: CryptoServiceItem): string {.base.} =
|
||||
return self.hostname
|
69
src/app/wallet/v2/views/buy_sell_crypto/service_model.nim
Normal file
69
src/app/wallet/v2/views/buy_sell_crypto/service_model.nim
Normal file
@ -0,0 +1,69 @@
|
||||
import NimQml, Tables
|
||||
|
||||
import service_item
|
||||
|
||||
type
|
||||
CryptoServiceModelRole {.pure.} = enum
|
||||
Name = UserRole + 1
|
||||
Description
|
||||
Fees
|
||||
LogoUrl
|
||||
SiteUrl
|
||||
Hostname
|
||||
|
||||
QtObject:
|
||||
type
|
||||
CryptoServiceModel* = ref object of QAbstractListModel
|
||||
list: seq[CryptoServiceItem]
|
||||
|
||||
proc delete(self: CryptoServiceModel) =
|
||||
self.QAbstractListModel.delete
|
||||
|
||||
proc setup(self: CryptoServiceModel) =
|
||||
self.QAbstractListModel.setup
|
||||
|
||||
proc newCryptoServiceModel*(): CryptoServiceModel =
|
||||
new(result, delete)
|
||||
result.setup()
|
||||
|
||||
method rowCount(self: CryptoServiceModel, index: QModelIndex = nil): int =
|
||||
return self.list.len
|
||||
|
||||
method roleNames(self: CryptoServiceModel): Table[int, string] =
|
||||
{
|
||||
CryptoServiceModelRole.Name.int:"name",
|
||||
CryptoServiceModelRole.Description.int:"description",
|
||||
CryptoServiceModelRole.Fees.int:"fees",
|
||||
CryptoServiceModelRole.LogoUrl.int:"logoUrl",
|
||||
CryptoServiceModelRole.SiteUrl.int:"siteUrl",
|
||||
CryptoServiceModelRole.Hostname.int:"hostname"
|
||||
}.toTable
|
||||
|
||||
method data(self: CryptoServiceModel, index: QModelIndex, role: int): QVariant =
|
||||
if (not index.isValid):
|
||||
return
|
||||
|
||||
if (index.row < 0 or index.row >= self.list.len):
|
||||
return
|
||||
|
||||
let item = self.list[index.row]
|
||||
let enumRole = role.CryptoServiceModelRole
|
||||
|
||||
case enumRole:
|
||||
of CryptoServiceModelRole.Name:
|
||||
result = newQVariant(item.getName)
|
||||
of CryptoServiceModelRole.Description:
|
||||
result = newQVariant(item.getDescription)
|
||||
of CryptoServiceModelRole.Fees:
|
||||
result = newQVariant(item.getFees)
|
||||
of CryptoServiceModelRole.LogoUrl:
|
||||
result = newQVariant(item.getLogoUrl)
|
||||
of CryptoServiceModelRole.SiteUrl:
|
||||
result = newQVariant(item.getSiteUrl)
|
||||
of CryptoServiceModelRole.Hostname:
|
||||
result = newQVariant(item.getHostname)
|
||||
|
||||
proc set*(self: CryptoServiceModel, items: seq[CryptoServiceItem]) =
|
||||
self.beginResetModel()
|
||||
self.list = items
|
||||
self.endResetModel()
|
@ -136,3 +136,12 @@ proc getOpenseaCollections*(address: string): string =
|
||||
proc getOpenseaAssets*(address: string, collectionSlug: string, limit: int): string =
|
||||
let payload = %* [address, collectionSlug, limit]
|
||||
result = callPrivateRPC("wallet_getOpenseaAssetsByOwnerAndCollection", payload)
|
||||
|
||||
proc fetchCryptoServices*(success: var bool): string =
|
||||
success = true
|
||||
try:
|
||||
result = callPrivateRPC("wallet_getCryptoOnRamps")
|
||||
except Exception as e:
|
||||
success = false
|
||||
error "Error getting crypto services: ", msg = e.msg
|
||||
result = ""
|
@ -21,6 +21,8 @@ from web3/conversions import `$`
|
||||
|
||||
export account, collectibles
|
||||
|
||||
include wallet2/async_tasks
|
||||
|
||||
logScope:
|
||||
topics = "status-wallet2"
|
||||
|
||||
@ -211,3 +213,22 @@ QtObject:
|
||||
|
||||
proc getOpenseaAssets*(address: string, collectionSlug: string, limit: int): string =
|
||||
result = status_wallet.getOpenseaAssets(address, collectionSlug, limit)
|
||||
|
||||
proc asyncFetchCryptoServices*(self: StatusWalletController) =
|
||||
## Asynchronous request for the list of services to buy/sell crypto.
|
||||
let arg = QObjectTaskArg(
|
||||
tptr: cast[ByteAddress](asyncGetCryptoServicesTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: "onAsyncFetchCryptoServices"
|
||||
)
|
||||
self.tasks.threadpool.start(arg)
|
||||
|
||||
proc onAsyncFetchCryptoServices*(self: StatusWalletController,
|
||||
response: string) {.slot.} =
|
||||
let responseArray = response.parseJson
|
||||
if (responseArray.kind != JArray):
|
||||
info "received crypto services is not a json array"
|
||||
self.events.emit("cryptoServicesFetched", CryptoServicesArg())
|
||||
return
|
||||
|
||||
self.events.emit("cryptoServicesFetched", CryptoServicesArg(services: responseArray))
|
16
src/status/wallet2/async_tasks.nim
Normal file
16
src/status/wallet2/async_tasks.nim
Normal file
@ -0,0 +1,16 @@
|
||||
include ../utils/json_utils
|
||||
|
||||
#################################################
|
||||
# Async request for the list of services to buy/sell crypto
|
||||
#################################################
|
||||
|
||||
const asyncGetCryptoServicesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||
let arg = decode[QObjectTaskArg](argEncoded)
|
||||
var success: bool
|
||||
let response = status_wallet.fetchCryptoServices(success)
|
||||
|
||||
var list: JsonNode
|
||||
if(success):
|
||||
list = response.parseJson()["result"]
|
||||
|
||||
arg.finish($list)
|
Loading…
x
Reference in New Issue
Block a user