feat: remove all remaining spawnAndSends
refactor: move threadpool task declarations inline with views Co-authored-by: Michael Bradley Jr. <michaelsbradleyjr@gmail.com> Co-authored-by: Eric Mastro <eric.mastro@gmail.com>
This commit is contained in:
parent
686cbc7f54
commit
1200632989
|
@ -14,18 +14,87 @@ import ../../status/ens as status_ens
|
|||
import ../../status/chat/[chat, message]
|
||||
import ../../status/profile/profile
|
||||
import web3/[conversions, ethtypes]
|
||||
import ../../status/threads
|
||||
import views/[channels_list, message_list, chat_item, suggestions_list, reactions, stickers, groups, transactions, communities, community_list, community_item]
|
||||
import json_serialization
|
||||
import ../utils/image_utils
|
||||
import ../../status/tasks/[qt, task_runner_impl]
|
||||
|
||||
logScope:
|
||||
topics = "chats-view"
|
||||
|
||||
|
||||
type
|
||||
ChatViewRoles {.pure.} = enum
|
||||
MessageList = UserRole + 1
|
||||
GetLinkPreviewDataTaskArg = ref object of QObjectTaskArg
|
||||
link: string
|
||||
uuid: string
|
||||
AsyncMessageLoadTaskArg = ref object of QObjectTaskArg
|
||||
chatId: string
|
||||
ResolveEnsTaskArg = ref object of QObjectTaskArg
|
||||
ens: string
|
||||
|
||||
const getLinkPreviewDataTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||
let arg = decode[GetLinkPreviewDataTaskArg](argEncoded)
|
||||
var success: bool
|
||||
# We need to call directly on libstatus because going through the status model is not thread safe
|
||||
let
|
||||
response = libstatus_chat.getLinkPreviewData(arg.link, success)
|
||||
responseJson = %* { "result": %response, "success": %success, "uuid": %arg.uuid }
|
||||
arg.finish(responseJson)
|
||||
|
||||
proc getLinkPreviewData[T](self: T, slot: string, link: string, uuid: string) =
|
||||
let arg = GetLinkPreviewDataTaskArg(
|
||||
tptr: cast[ByteAddress](getLinkPreviewDataTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: slot,
|
||||
link: link,
|
||||
uuid: uuid
|
||||
)
|
||||
self.status.tasks.threadpool.start(arg)
|
||||
|
||||
const asyncMessageLoadTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||
let arg = decode[AsyncMessageLoadTaskArg](argEncoded)
|
||||
var messages: JsonNode
|
||||
var msgCallSuccess: bool
|
||||
let msgCallResult = rpcChatMessages(arg.chatId, newJString(""), 20, msgCallSuccess)
|
||||
if(msgCallSuccess):
|
||||
messages = msgCallResult.parseJson()["result"]
|
||||
|
||||
var reactions: JsonNode
|
||||
var reactionsCallSuccess: bool
|
||||
let reactionsCallResult = rpcReactions(arg.chatId, newJString(""), 20, reactionsCallSuccess)
|
||||
if(reactionsCallSuccess):
|
||||
reactions = reactionsCallResult.parseJson()["result"]
|
||||
|
||||
let responseJson = %*{
|
||||
"chatId": arg.chatId,
|
||||
"messages": messages,
|
||||
"reactions": reactions
|
||||
}
|
||||
arg.finish(responseJson)
|
||||
|
||||
proc asyncMessageLoad[T](self: T, slot: string, chatId: string) =
|
||||
let arg = AsyncMessageLoadTaskArg(
|
||||
tptr: cast[ByteAddress](asyncMessageLoadTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: slot,
|
||||
chatId: chatId
|
||||
)
|
||||
self.status.tasks.threadpool.start(arg)
|
||||
|
||||
const resolveEnsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||
let
|
||||
arg = decode[ResolveEnsTaskArg](argEncoded)
|
||||
result = status_ens.pubkey(arg.ens)
|
||||
arg.finish(result)
|
||||
|
||||
proc resolveEns[T](self: T, slot: string, ens: string) =
|
||||
let arg = ResolveEnsTaskArg(
|
||||
tptr: cast[ByteAddress](asyncMessageLoadTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: slot,
|
||||
ens: ens
|
||||
)
|
||||
self.status.tasks.threadpool.start(arg)
|
||||
|
||||
QtObject:
|
||||
type
|
||||
|
@ -487,11 +556,7 @@ QtObject:
|
|||
self.linkPreviewDataWasReceived(previewData)
|
||||
|
||||
proc getLinkPreviewData*(self: ChatsView, link: string, uuid: string) {.slot.} =
|
||||
spawnAndSend(self, "linkPreviewDataReceived") do:
|
||||
var success: bool
|
||||
# We need to call directly on libstatus because going through the status model is not thread safe
|
||||
let response = libstatus_chat.getLinkPreviewData(link, success)
|
||||
$(%* { "result": %response, "success": %success, "uuid": %uuid })
|
||||
self.getLinkPreviewData("linkPreviewDataReceived", link, uuid)
|
||||
|
||||
proc joinChat*(self: ChatsView, channel: string, chatTypeInt: int): int {.slot.} =
|
||||
self.status.chat.join(channel, ChatType(chatTypeInt))
|
||||
|
@ -524,26 +589,7 @@ QtObject:
|
|||
proc loadingMessagesChanged*(self: ChatsView, value: bool) {.signal.}
|
||||
|
||||
proc asyncMessageLoad*(self: ChatsView, chatId: string) {.slot.} =
|
||||
spawnAndSend(self, "asyncMessageLoaded") do: # Call self.ensResolved(string) when ens is resolved
|
||||
|
||||
var messages: JsonNode
|
||||
var msgCallSuccess: bool
|
||||
let msgCallResult = rpcChatMessages(chatId, newJString(""), 20, msgCallSuccess)
|
||||
if(msgCallSuccess):
|
||||
messages = msgCallResult.parseJson()["result"]
|
||||
|
||||
var reactions: JsonNode
|
||||
var reactionsCallSuccess: bool
|
||||
let reactionsCallResult = rpcReactions(chatId, newJString(""), 20, reactionsCallSuccess)
|
||||
if(reactionsCallSuccess):
|
||||
reactions = reactionsCallResult.parseJson()["result"]
|
||||
|
||||
|
||||
$(%*{
|
||||
"chatId": chatId,
|
||||
"messages": messages,
|
||||
"reactions": reactions
|
||||
})
|
||||
self.asyncMessageLoad("asyncMessageLoaded", chatId)
|
||||
|
||||
proc asyncMessageLoaded*(self: ChatsView, rpcResponse: string) {.slot.} =
|
||||
let rpcResponseObj = rpcResponse.parseJson
|
||||
|
@ -677,8 +723,7 @@ QtObject:
|
|||
|
||||
# Resolving a ENS name
|
||||
proc resolveENS*(self: ChatsView, ens: string) {.slot.} =
|
||||
spawnAndSend(self, "ensResolved") do: # Call self.ensResolved(string) when ens is resolved
|
||||
status_ens.pubkey(ens)
|
||||
self.resolveEns("ensResolved", ens) # Call self.ensResolved(string) when ens is resolved
|
||||
|
||||
proc ensWasResolved*(self: ChatsView, resolvedPubKey: string) {.signal.}
|
||||
|
||||
|
|
|
@ -1,15 +1,67 @@
|
|||
import NimQml, tables, json, chronicles, sets, strutils
|
||||
import ../../../status/[status, stickers, threads]
|
||||
import ../../../status/[status, stickers]
|
||||
import ../../../status/libstatus/[types, utils]
|
||||
import ../../../status/libstatus/stickers as status_stickers
|
||||
import ../../../status/libstatus/wallet as status_wallet
|
||||
import sticker_pack_list, sticker_list, chat_item
|
||||
import json_serialization
|
||||
import ../../../status/tasks/task_manager
|
||||
import ../../../status/tasks/[qt, task_runner_impl]
|
||||
|
||||
logScope:
|
||||
topics = "stickers-view"
|
||||
|
||||
type
|
||||
EstimateTaskArg = ref object of QObjectTaskArg
|
||||
packId: int
|
||||
address: string
|
||||
price: string
|
||||
uuid: string
|
||||
ObtainAvailableStickerPacksTaskArg = ref object of QObjectTaskArg
|
||||
|
||||
# The pragmas `{.gcsafe, nimcall.}` in this context do not force the compiler
|
||||
# to accept unsafe code, rather they work in conjunction with the proc
|
||||
# signature for `type Task` in tasks/common.nim to ensure that the proc really
|
||||
# is gcsafe and that a helpful error message is displayed
|
||||
const estimateTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||
let arg = decode[EstimateTaskArg](argEncoded)
|
||||
var success: bool
|
||||
var estimate = estimateGas(arg.packId, arg.address, arg.price,
|
||||
success)
|
||||
if not success:
|
||||
estimate = 325000
|
||||
let tpl: tuple[estimate: int, uuid: string] = (estimate, arg.uuid)
|
||||
arg.finish(tpl)
|
||||
|
||||
# the [T] here is annoying but the QtObject template only allows for one type
|
||||
# definition so we'll need to setup the type, task, and helper outside of body
|
||||
# passed to `QtObject:`
|
||||
proc estimate[T](self: T, slot: string, packId: int, address: string, price: string,
|
||||
uuid: string) =
|
||||
let arg = EstimateTaskArg(
|
||||
tptr: cast[ByteAddress](estimateTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: slot,
|
||||
packId: packId,
|
||||
address: address,
|
||||
price: price,
|
||||
uuid: uuid
|
||||
)
|
||||
self.status.tasks.threadpool.start(arg)
|
||||
|
||||
const obtainAvailableStickerPacksTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||
let arg = decode[ObtainAvailableStickerPacksTaskArg](argEncoded)
|
||||
let availableStickerPacks = status_stickers.getAvailableStickerPacks()
|
||||
var packs: seq[StickerPack] = @[]
|
||||
for packId, stickerPack in availableStickerPacks.pairs:
|
||||
packs.add(stickerPack)
|
||||
arg.finish(%*(packs))
|
||||
|
||||
proc obtainAvailableStickerPacks[T](self: T, slot: string) =
|
||||
let arg = ObtainAvailableStickerPacksTaskArg(
|
||||
tptr: cast[ByteAddress](obtainAvailableStickerPacksTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: slot)
|
||||
self.status.tasks.threadpool.start(arg)
|
||||
|
||||
QtObject:
|
||||
type StickersView* = ref object of QObject
|
||||
status: Status
|
||||
|
@ -46,7 +98,7 @@ QtObject:
|
|||
proc transactionCompleted*(self: StickersView, success: bool, txHash: string, revertReason: string = "") {.signal.}
|
||||
|
||||
proc estimate*(self: StickersView, packId: int, address: string, price: string, uuid: string) {.slot.} =
|
||||
self.status.taskManager.threadPool.stickers.stickerPackPurchaseGasEstimate(cast[pointer](self.vptr), "setGasEstimate", packId, address, price, uuid)
|
||||
self.estimate("setGasEstimate", packId, address, price, uuid)
|
||||
|
||||
proc gasEstimateReturned*(self: StickersView, estimate: int, uuid: string) {.signal.}
|
||||
|
||||
|
@ -65,7 +117,7 @@ QtObject:
|
|||
self.transactionWasSent(response)
|
||||
|
||||
proc obtainAvailableStickerPacks*(self: StickersView) =
|
||||
self.status.taskManager.threadPool.stickers.obtainAvailableStickerPacks(cast[pointer](self.vptr), "setAvailableStickerPacks")
|
||||
self.obtainAvailableStickerPacks("setAvailableStickerPacks")
|
||||
|
||||
proc stickerPacksLoaded*(self: StickersView) {.signal.}
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ import ../../status/libstatus/settings as status_settings
|
|||
import ../../status/status
|
||||
import ../../status/ens as status_ens
|
||||
import ../../status/chat/chat
|
||||
import ../../status/threads
|
||||
import ../../status/libstatus/types
|
||||
import ../../status/libstatus/accounts/constants as accountConstants
|
||||
import qrcode/qrcode
|
||||
|
|
|
@ -1,15 +1,35 @@
|
|||
import NimQml, chronicles, sequtils, sugar, strutils
|
||||
import ../../../status/libstatus/utils as status_utils
|
||||
import ../../../status/status
|
||||
import ../../../status/threads
|
||||
import ../../../status/chat/chat
|
||||
import contact_list
|
||||
import ../../../status/profile/profile
|
||||
import ../../../status/ens as status_ens
|
||||
import ../../../status/tasks/[qt, task_runner_impl]
|
||||
|
||||
logScope:
|
||||
topics = "contacts-view"
|
||||
|
||||
type
|
||||
LookupContactTaskArg = ref object of QObjectTaskArg
|
||||
value: string
|
||||
|
||||
const lookupContactTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||
let arg = decode[LookupContactTaskArg](argEncoded)
|
||||
var id = arg.value
|
||||
if not id.startsWith("0x"):
|
||||
id = status_ens.pubkey(id)
|
||||
arg.finish(id)
|
||||
|
||||
proc lookupContact[T](self: T, slot: string, value: string) =
|
||||
let arg = LookupContactTaskArg(
|
||||
tptr: cast[ByteAddress](lookupContactTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: slot,
|
||||
value: value
|
||||
)
|
||||
self.status.tasks.threadpool.start(arg)
|
||||
|
||||
QtObject:
|
||||
type ContactsView* = ref object of QObject
|
||||
status: Status
|
||||
|
@ -113,11 +133,7 @@ QtObject:
|
|||
if value == "":
|
||||
return
|
||||
|
||||
spawnAndSend(self, "ensResolved") do: # Call self.ensResolved(string) when ens is resolved
|
||||
var id = value
|
||||
if not id.startsWith("0x"):
|
||||
id = status_ens.pubkey(id)
|
||||
id
|
||||
self.lookupContact("ensResolved", value)
|
||||
|
||||
proc ensWasResolved*(self: ContactsView, resolvedPubKey: string) {.signal.}
|
||||
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import NimQml
|
||||
import Tables
|
||||
import json
|
||||
import json_serialization
|
||||
import sequtils
|
||||
import strutils
|
||||
from ../../../status/libstatus/types import Setting, PendingTransactionType, RpcException
|
||||
import ../../../status/threads
|
||||
import ../../../status/ens as status_ens
|
||||
import ../../../status/libstatus/wallet as status_wallet
|
||||
import ../../../status/libstatus/settings as status_settings
|
||||
|
@ -14,11 +12,78 @@ import ../../../status/status
|
|||
import ../../../status/wallet
|
||||
import sets
|
||||
import web3/ethtypes
|
||||
import ../../../status/tasks/[qt, task_runner_impl]
|
||||
|
||||
type
|
||||
EnsRoles {.pure.} = enum
|
||||
UserName = UserRole + 1
|
||||
IsPending = UserRole + 2
|
||||
ValidateTaskArg = ref object of QObjectTaskArg
|
||||
ens: string
|
||||
isStatus: bool
|
||||
usernames: seq[string]
|
||||
DetailsTaskArg = ref object of QObjectTaskArg
|
||||
username: string
|
||||
|
||||
const validateTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||
let
|
||||
arg = decode[ValidateTaskArg](argEncoded)
|
||||
username = arg.ens & (if(arg.isStatus): status_ens.domain else: "")
|
||||
var output = ""
|
||||
if arg.usernames.filter(proc(x: string):bool = x == username).len > 0:
|
||||
output = "already-connected"
|
||||
else:
|
||||
let ownerAddr = status_ens.owner(username)
|
||||
if ownerAddr == "" and arg.isStatus:
|
||||
output = "available"
|
||||
else:
|
||||
let userPubKey = status_settings.getSetting[string](Setting.PublicKey, "0x0")
|
||||
let userWallet = status_wallet.getWalletAccounts()[0].address
|
||||
let pubkey = status_ens.pubkey(arg.ens)
|
||||
if ownerAddr != "":
|
||||
if pubkey == "" and ownerAddr == userWallet:
|
||||
output = "owned" # "Continuing will connect this username with your chat key."
|
||||
elif pubkey == userPubkey:
|
||||
output = "connected"
|
||||
elif ownerAddr == userWallet:
|
||||
output = "connected-different-key" # "Continuing will require a transaction to connect the username with your current chat key.",
|
||||
else:
|
||||
output = "taken"
|
||||
else:
|
||||
output = "taken"
|
||||
arg.finish(output)
|
||||
|
||||
proc validate[T](self: T, slot: string, ens: string, isStatus: bool, usernames: seq[string]) =
|
||||
let arg = ValidateTaskArg(
|
||||
tptr: cast[ByteAddress](validateTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: slot,
|
||||
ens: ens,
|
||||
isStatus: isStatus,
|
||||
usernames: usernames
|
||||
)
|
||||
self.status.tasks.threadpool.start(arg)
|
||||
|
||||
const detailsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||
let
|
||||
arg = decode[DetailsTaskArg](argEncoded)
|
||||
address = status_ens.address(arg.username)
|
||||
pubkey = status_ens.pubkey(arg.username)
|
||||
json = %* {
|
||||
"ensName": arg.username,
|
||||
"address": address,
|
||||
"pubkey": pubkey
|
||||
}
|
||||
arg.finish(json)
|
||||
|
||||
proc details[T](self: T, slot: string, username: string) =
|
||||
let arg = DetailsTaskArg(
|
||||
tptr: cast[ByteAddress](detailsTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: slot,
|
||||
username: username
|
||||
)
|
||||
self.status.tasks.threadpool.start(arg)
|
||||
|
||||
QtObject:
|
||||
type EnsManager* = ref object of QAbstractListModel
|
||||
|
@ -58,31 +123,7 @@ QtObject:
|
|||
self.ensWasResolved(ensResult)
|
||||
|
||||
proc validate*(self: EnsManager, ens: string, isStatus: bool) {.slot.} =
|
||||
let username = ens & (if(isStatus): status_ens.domain else: "")
|
||||
if self.usernames.filter(proc(x: string):bool = x == username).len > 0:
|
||||
self.ensResolved("already-connected")
|
||||
else:
|
||||
spawnAndSend(self, "ensResolved") do:
|
||||
let ownerAddr = status_ens.owner(username)
|
||||
var output = ""
|
||||
if ownerAddr == "" and isStatus:
|
||||
output = "available"
|
||||
else:
|
||||
let userPubKey = status_settings.getSetting[string](Setting.PublicKey, "0x0")
|
||||
let userWallet = status_wallet.getWalletAccounts()[0].address
|
||||
let pubkey = status_ens.pubkey(ens)
|
||||
if ownerAddr != "":
|
||||
if pubkey == "" and ownerAddr == userWallet:
|
||||
output = "owned" # "Continuing will connect this username with your chat key."
|
||||
elif pubkey == userPubkey:
|
||||
output = "connected"
|
||||
elif ownerAddr == userWallet:
|
||||
output = "connected-different-key" # "Continuing will require a transaction to connect the username with your current chat key.",
|
||||
else:
|
||||
output = "taken"
|
||||
else:
|
||||
output = "taken"
|
||||
output
|
||||
self.validate("ensResolved", ens, isStatus, self.usernames)
|
||||
|
||||
proc add*(self: EnsManager, username: string) =
|
||||
self.beginInsertRows(newQModelIndex(), self.usernames.len, self.usernames.len)
|
||||
|
@ -119,14 +160,7 @@ QtObject:
|
|||
|
||||
proc details(self: EnsManager, username: string) {.slot.} =
|
||||
self.loading(true)
|
||||
spawnAndSend(self, "setDetails") do:
|
||||
let address = status_ens.address(username)
|
||||
let pubkey = status_ens.pubkey(username)
|
||||
$(%* {
|
||||
"ensName": username,
|
||||
"address": address,
|
||||
"pubkey": pubkey
|
||||
})
|
||||
self.details("setDetails", username)
|
||||
|
||||
proc detailsObtained(self: EnsManager, ensName: string, address: string, pubkey: string) {.signal.}
|
||||
|
||||
|
|
|
@ -129,7 +129,7 @@ QtObject:
|
|||
|
||||
var success: bool
|
||||
# TODO make this async
|
||||
let response = status.wallet.sendTransaction(fromAddress, to, value, selectedGasLimit, selectedGasPrice, password, success, txData)
|
||||
let response = wallet.sendTransaction(fromAddress, to, value, selectedGasLimit, selectedGasPrice, password, success, txData)
|
||||
let errorMessage = if not success:
|
||||
if response == "":
|
||||
"web3-response-error"
|
||||
|
|
|
@ -71,7 +71,7 @@ proc init*(self: WalletController) =
|
|||
self.view.transactionCompleted(tx.success, tx.transactionHash, tx.revertReason)
|
||||
|
||||
proc checkPendingTransactions*(self: WalletController) =
|
||||
self.status.wallet.checkPendingTransactions() # TODO: consider doing this in a spawnAndSend
|
||||
self.status.wallet.checkPendingTransactions() # TODO: consider doing this in a threadpool task
|
||||
|
||||
proc start*(self: WalletController) =
|
||||
status_wallet.startWallet(false)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import NimQml, Tables, strformat, strutils, chronicles, sequtils, json, std/wrapnils, parseUtils, stint, tables, json_serialization
|
||||
import ../../status/[status, wallet, threads]
|
||||
import NimQml, Tables, strformat, strutils, chronicles, sequtils, json, std/wrapnils, parseUtils, stint, tables
|
||||
import ../../status/[status, wallet]
|
||||
import ../../status/wallet/collectibles as status_collectibles
|
||||
import ../../status/libstatus/accounts/constants
|
||||
import ../../status/libstatus/wallet as status_wallet
|
||||
|
@ -10,6 +10,160 @@ import ../../status/libstatus/utils as status_utils
|
|||
import ../../status/libstatus/eth/contracts
|
||||
import ../../status/ens as status_ens
|
||||
import views/[asset_list, account_list, account_item, token_list, transaction_list, collectibles_list]
|
||||
import ../../status/tasks/[qt, task_runner_impl]
|
||||
|
||||
type
|
||||
SendTransactionTaskArg = ref object of QObjectTaskArg
|
||||
from_addr: string
|
||||
to: string
|
||||
assetAddress: string
|
||||
value: string
|
||||
gas: string
|
||||
gasPrice: string
|
||||
password: string
|
||||
uuid: string
|
||||
InitBalancesTaskArg = ref object of QObjectTaskArg
|
||||
address: string
|
||||
tokenList: seq[string]
|
||||
LoadCollectiblesTaskArg = ref object of QObjectTaskArg
|
||||
address: string
|
||||
collectiblesType: string
|
||||
GasPredictionsTaskArg = ref object of QObjectTaskArg
|
||||
LoadTransactionsTaskArg = ref object of QObjectTaskArg
|
||||
address: string
|
||||
blockNumber: string
|
||||
ResolveEnsTaskArg = ref object of QObjectTaskArg
|
||||
ens: string
|
||||
uuid: string
|
||||
|
||||
const sendTransactionTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||
let arg = decode[SendTransactionTaskArg](argEncoded)
|
||||
var
|
||||
success: bool
|
||||
response: string
|
||||
if arg.assetAddress != ZERO_ADDRESS and not arg.assetAddress.isEmptyOrWhitespace:
|
||||
response = wallet.sendTokenTransaction(arg.from_addr, arg.to, arg.assetAddress, arg.value, arg.gas, arg.gasPrice, arg.password, success)
|
||||
else:
|
||||
response = wallet.sendTransaction(arg.from_addr, arg.to, arg.value, arg.gas, arg.gasPrice, arg.password, success)
|
||||
let output = %* { "result": %response, "success": %success, "uuid": %arg.uuid }
|
||||
arg.finish(output)
|
||||
|
||||
proc sendTransaction[T](self: T, slot: string, from_addr: string, to: string, assetAddress: string, value: string, gas: string, gasPrice: string, password: string, uuid: string) =
|
||||
let arg = SendTransactionTaskArg(
|
||||
tptr: cast[ByteAddress](sendTransactionTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: slot,
|
||||
from_addr: from_addr,
|
||||
to: to,
|
||||
assetAddress: assetAddress,
|
||||
value: value,
|
||||
gas: gas,
|
||||
gasPrice: gasPrice,
|
||||
password: password,
|
||||
uuid: uuid
|
||||
)
|
||||
self.status.tasks.threadpool.start(arg)
|
||||
|
||||
const initBalancesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||
let arg = decode[InitBalancesTaskArg](argEncoded)
|
||||
var tokenBalances = initTable[string, string]()
|
||||
for token in arg.tokenList:
|
||||
tokenBalances[token] = getTokenBalance(token, arg.address)
|
||||
let output = %* {
|
||||
"address": arg.address,
|
||||
"eth": getEthBalance(arg.address),
|
||||
"tokens": tokenBalances
|
||||
}
|
||||
arg.finish(output)
|
||||
|
||||
proc initBalances[T](self: T, slot: string, address: string, tokenList: seq[string]) =
|
||||
let arg = InitBalancesTaskArg(
|
||||
tptr: cast[ByteAddress](initBalancesTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: slot,
|
||||
address: address,
|
||||
tokenList: tokenList
|
||||
)
|
||||
self.status.tasks.threadpool.start(arg)
|
||||
|
||||
const loadCollectiblesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||
let arg = decode[LoadCollectiblesTaskArg](argEncoded)
|
||||
var collectiblesOrError = ""
|
||||
case arg.collectiblesType:
|
||||
of status_collectibles.CRYPTOKITTY:
|
||||
collectiblesOrError = status_collectibles.getCryptoKitties(arg.address)
|
||||
of status_collectibles.KUDO:
|
||||
collectiblesOrError = status_collectibles.getKudos(arg.address)
|
||||
of status_collectibles.ETHERMON:
|
||||
collectiblesOrError = status_collectibles.getEthermons(arg.address)
|
||||
of status_collectibles.STICKER:
|
||||
collectiblesOrError = status_collectibles.getStickers(arg.address)
|
||||
|
||||
let output = %*{
|
||||
"address": arg.address,
|
||||
"collectibleType": arg.collectiblesType,
|
||||
"collectiblesOrError": collectiblesOrError
|
||||
}
|
||||
arg.finish(output)
|
||||
|
||||
proc loadCollectibles[T](self: T, slot: string, address: string, collectiblesType: string) =
|
||||
let arg = LoadCollectiblesTaskArg(
|
||||
tptr: cast[ByteAddress](loadCollectiblesTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: slot,
|
||||
address: address,
|
||||
collectiblesType: collectiblesType,
|
||||
)
|
||||
self.status.tasks.threadpool.start(arg)
|
||||
|
||||
const getGasPredictionsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||
let
|
||||
arg = decode[GasPredictionsTaskArg](argEncoded)
|
||||
output = %getGasPricePredictions()
|
||||
arg.finish(output)
|
||||
|
||||
proc getGasPredictions[T](self: T, slot: string) =
|
||||
let arg = GasPredictionsTaskArg(
|
||||
tptr: cast[ByteAddress](getGasPredictionsTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: slot
|
||||
)
|
||||
self.status.tasks.threadpool.start(arg)
|
||||
|
||||
const loadTransactionsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||
let
|
||||
arg = decode[LoadTransactionsTaskArg](argEncoded)
|
||||
output = %*{
|
||||
"address": arg.address,
|
||||
"history": getTransfersByAddress(arg.address, arg.blockNumber)
|
||||
}
|
||||
arg.finish(output)
|
||||
|
||||
proc loadTransactions[T](self: T, slot: string, address: string, blockNumber: string) =
|
||||
let arg = LoadTransactionsTaskArg(
|
||||
tptr: cast[ByteAddress](loadTransactionsTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: slot,
|
||||
address: address,
|
||||
blockNumber: blockNumber
|
||||
)
|
||||
self.status.tasks.threadpool.start(arg)
|
||||
|
||||
const resolveEnsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||
let
|
||||
arg = decode[ResolveEnsTaskArg](argEncoded)
|
||||
output = %* { "address": status_ens.address(arg.ens), "uuid": arg.uuid }
|
||||
arg.finish(output)
|
||||
|
||||
proc resolveEns[T](self: T, slot: string, ens: string, uuid: string) =
|
||||
let arg = ResolveEnsTaskArg(
|
||||
tptr: cast[ByteAddress](resolveEnsTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: slot,
|
||||
ens: ens,
|
||||
uuid: uuid
|
||||
)
|
||||
self.status.tasks.threadpool.start(arg)
|
||||
|
||||
QtObject:
|
||||
type
|
||||
|
@ -60,8 +214,8 @@ QtObject:
|
|||
result.currentAssetList = newAssetList()
|
||||
result.currentTransactions = newTransactionList()
|
||||
result.currentCollectiblesLists = newCollectiblesList()
|
||||
result.defaultTokenList = newTokenList()
|
||||
result.customTokenList = newTokenList()
|
||||
result.defaultTokenList = newTokenList(status)
|
||||
result.customTokenList = newTokenList(status)
|
||||
result.totalFiatBalance = ""
|
||||
result.etherscanLink = ""
|
||||
result.safeLowGasPrice = "0"
|
||||
|
@ -292,17 +446,7 @@ QtObject:
|
|||
self.transactionWasSent(txResult)
|
||||
|
||||
proc sendTransaction*(self: WalletView, from_addr: string, to: string, assetAddress: string, value: string, gas: string, gasPrice: string, password: string, uuid: string) {.slot.} =
|
||||
let wallet = self.status.wallet
|
||||
if assetAddress != ZERO_ADDRESS and not assetAddress.isEmptyOrWhitespace:
|
||||
spawnAndSend(self, "transactionSent") do:
|
||||
var success: bool
|
||||
let response = wallet.sendTokenTransaction(from_addr, to, assetAddress, value, gas, gasPrice, password, success)
|
||||
$(%* { "result": %response, "success": %success, "uuid": %uuid })
|
||||
else:
|
||||
spawnAndSend(self, "transactionSent") do:
|
||||
var success: bool
|
||||
let response = wallet.sendTransaction(from_addr, to, value, gas, gasPrice, password, success)
|
||||
$(%* { "result": %response, "success": %success, "uuid": %uuid })
|
||||
self.sendTransaction("transactionSent", from_addr, to, assetAddress, value, gas, gasPrice, password, uuid)
|
||||
|
||||
proc getDefaultAccount*(self: WalletView): string {.slot.} =
|
||||
self.currentAccount.address
|
||||
|
@ -364,15 +508,7 @@ QtObject:
|
|||
for acc in self.status.wallet.accounts:
|
||||
let accountAddress = acc.address
|
||||
let tokenList = acc.assetList.filter(proc(x:Asset): bool = x.address != "").map(proc(x: Asset): string = x.address)
|
||||
spawnAndSend(self, "getAccountBalanceSuccess") do:
|
||||
var tokenBalances = initTable[string, string]()
|
||||
for token in tokenList:
|
||||
tokenBalances[token] = getTokenBalance(token, accountAddress)
|
||||
$ %* {
|
||||
"address": accountAddress,
|
||||
"eth": getEthBalance(accountAddress),
|
||||
"tokens": tokenBalances
|
||||
}
|
||||
self.initBalances("getAccountBalanceSuccess", accountAddress, tokenList)
|
||||
|
||||
proc getAccountBalanceSuccess*(self: WalletView, jsonResponse: string) {.slot.} =
|
||||
let jsonObj = jsonResponse.parseJson()
|
||||
|
@ -396,31 +532,11 @@ QtObject:
|
|||
))
|
||||
|
||||
# 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,
|
||||
"collectibleType": status_collectibles.CRYPTOKITTY,
|
||||
"collectiblesOrError": status_collectibles.getCryptoKitties(address)
|
||||
})
|
||||
spawnAndSend(self, "setCollectiblesResult") do:
|
||||
$(%*{
|
||||
"address": address,
|
||||
"collectibleType": status_collectibles.KUDO,
|
||||
"collectiblesOrError": status_collectibles.getKudos(address)
|
||||
})
|
||||
spawnAndSend(self, "setCollectiblesResult") do:
|
||||
$(%*{
|
||||
"address": address,
|
||||
"collectibleType": status_collectibles.ETHERMON,
|
||||
"collectiblesOrError": status_collectibles.getEthermons(address)
|
||||
})
|
||||
spawnAndSend(self, "setCollectiblesResult") do:
|
||||
$(%*{
|
||||
"address": address,
|
||||
"collectibleType": status_collectibles.STICKER,
|
||||
"collectiblesOrError": status_collectibles.getStickers(address)
|
||||
})
|
||||
# Create a thread in the threadpool for each collectible. They can end in whichever order
|
||||
self.loadCollectibles("setCollectiblesResult", address, status_collectibles.CRYPTOKITTY)
|
||||
self.loadCollectibles("setCollectiblesResult", address, status_collectibles.KUDO)
|
||||
self.loadCollectibles("setCollectiblesResult", address, status_collectibles.ETHERMON)
|
||||
self.loadCollectibles("setCollectiblesResult", address, status_collectibles.STICKER)
|
||||
|
||||
proc setCollectiblesResult(self: WalletView, collectiblesJSON: string) {.slot.} =
|
||||
let collectibleData = parseJson(collectiblesJSON)
|
||||
|
@ -452,47 +568,13 @@ QtObject:
|
|||
|
||||
proc reloadCollectible*(self: WalletView, collectibleType: string) {.slot.} =
|
||||
let address = self.currentAccount.address
|
||||
# TODO find a cooler way to do this
|
||||
case collectibleType:
|
||||
of CRYPTOKITTY:
|
||||
spawnAndSend(self, "setCollectiblesResult") do:
|
||||
$(%*{
|
||||
"address": address,
|
||||
"collectibleType": status_collectibles.CRYPTOKITTY,
|
||||
"collectiblesOrError": status_collectibles.getCryptoKitties(address)
|
||||
})
|
||||
of KUDO:
|
||||
spawnAndSend(self, "setCollectiblesResult") do:
|
||||
$(%*{
|
||||
"address": address,
|
||||
"collectibleType": status_collectibles.KUDO,
|
||||
"collectiblesOrError": status_collectibles.getKudos(address)
|
||||
})
|
||||
of ETHERMON:
|
||||
spawnAndSend(self, "setCollectiblesResult") do:
|
||||
$(%*{
|
||||
"address": address,
|
||||
"collectibleType": status_collectibles.ETHERMON,
|
||||
"collectiblesOrError": status_collectibles.getEthermons(address)
|
||||
})
|
||||
of status_collectibles.STICKER:
|
||||
spawnAndSend(self, "setCollectiblesResult") do:
|
||||
$(%*{
|
||||
"address": address,
|
||||
"collectibleType": status_collectibles.STICKER,
|
||||
"collectiblesOrError": status_collectibles.getStickers(address)
|
||||
})
|
||||
else:
|
||||
error "Unrecognized collectible"
|
||||
return
|
||||
|
||||
self.loadCollectibles("setCollectiblesResult", address, collectibleType)
|
||||
self.currentCollectiblesLists.setLoadingByType(collectibleType, 1)
|
||||
|
||||
proc gasPricePredictionsChanged*(self: WalletView) {.signal.}
|
||||
|
||||
proc getGasPricePredictions*(self: WalletView) {.slot.} =
|
||||
spawnAndSend(self, "getGasPricePredictionsResult") do:
|
||||
$ %getGasPricePredictions2()
|
||||
self.getGasPredictions("getGasPricePredictionsResult")
|
||||
|
||||
proc getGasPricePredictionsResult(self: WalletView, gasPricePredictionsJson: string) {.slot.} =
|
||||
let prediction = Json.decode(gasPricePredictionsJson, GasPricePrediction)
|
||||
|
@ -586,11 +668,7 @@ QtObject:
|
|||
# spawn'ed function cannot have a 'var' parameter
|
||||
let blockNumber = bn
|
||||
self.loadingTrxHistoryChanged(true)
|
||||
spawnAndSend(self, "setTrxHistoryResult") do:
|
||||
$(%*{
|
||||
"address": address,
|
||||
"history": getTransfersByAddress(address, blockNumber)
|
||||
})
|
||||
self.loadTransactions("setTrxHistoryResult", address, bn)
|
||||
|
||||
proc setTrxHistoryResult(self: WalletView, historyJSON: string) {.slot.} =
|
||||
let historyData = parseJson(historyJSON)
|
||||
|
@ -605,8 +683,7 @@ QtObject:
|
|||
self.loadingTrxHistoryChanged(false)
|
||||
|
||||
proc resolveENS*(self: WalletView, ens: string, uuid: string) {.slot.} =
|
||||
spawnAndSend(self, "ensResolved") do:
|
||||
$ %* { "address": status_ens.address(ens), "uuid": uuid }
|
||||
self.resolveEns("ensResolved", ens, uuid)
|
||||
|
||||
proc ensWasResolved*(self: WalletView, resolvedAddress: string, uuid: string) {.signal.}
|
||||
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
import NimQml, tables, json
|
||||
import ../../../status/libstatus/[tokens, settings, utils, eth/contracts]
|
||||
import # nim libs
|
||||
tables, json
|
||||
|
||||
import # vendor libs
|
||||
NimQml
|
||||
|
||||
import # status-desktop libs
|
||||
../../../status/libstatus/[tokens, settings, utils, eth/contracts],
|
||||
../../../status/tasks/[qt, task_runner_impl], ../../../status/status
|
||||
from web3/conversions import `$`
|
||||
import ../../../status/threads
|
||||
|
||||
type
|
||||
TokenRoles {.pure.} = enum
|
||||
|
@ -11,9 +17,34 @@ type
|
|||
Address = UserRole + 4,
|
||||
Decimals = UserRole + 5
|
||||
IsCustom = UserRole + 6
|
||||
GetTokenDetailsTaskArg = ref object of QObjectTaskArg
|
||||
address: string
|
||||
|
||||
const getTokenDetailsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||
let
|
||||
arg = decode[GetTokenDetailsTaskArg](argEncoded)
|
||||
tkn = newErc20Contract(getCurrentNetwork(), arg.address.parseAddress)
|
||||
decimals = tkn.tokenDecimals()
|
||||
|
||||
let output = %* {
|
||||
"address": arg.address,
|
||||
"name": tkn.tokenName(),
|
||||
"symbol": tkn.tokenSymbol(),
|
||||
"decimals": (if decimals == 0: "" else: $decimals)
|
||||
}
|
||||
arg.finish(output)
|
||||
|
||||
proc getTokenDetails[T](self: T, slot: string, address: string) =
|
||||
let arg = GetTokenDetailsTaskArg(
|
||||
tptr: cast[ByteAddress](getTokenDetailsTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: slot,
|
||||
address: address)
|
||||
self.status.tasks.threadpool.start(arg)
|
||||
|
||||
QtObject:
|
||||
type TokenList* = ref object of QAbstractListModel
|
||||
status*: Status
|
||||
tokens*: seq[Erc20Contract]
|
||||
isCustom*: bool
|
||||
|
||||
|
@ -39,9 +70,10 @@ QtObject:
|
|||
self.isCustom = true
|
||||
self.endResetModel()
|
||||
|
||||
proc newTokenList*(): TokenList =
|
||||
proc newTokenList*(status: Status): TokenList =
|
||||
new(result, delete)
|
||||
result.tokens = @[]
|
||||
result.status = status
|
||||
result.setup
|
||||
|
||||
proc rowData(self: TokenList, index: int, column: string): string {.slot.} =
|
||||
|
@ -82,17 +114,7 @@ QtObject:
|
|||
TokenRoles.IsCustom.int:"isCustom"}.toTable
|
||||
|
||||
proc getTokenDetails*(self: TokenList, address: string) {.slot.} =
|
||||
spawnAndSend(self, "tokenDetailsResolved") do:
|
||||
let tkn = newErc20Contract(getCurrentNetwork(), address.parseAddress)
|
||||
|
||||
let decimals = tkn.tokenDecimals()
|
||||
|
||||
$ (%* {
|
||||
"address": address,
|
||||
"name": tkn.tokenName(),
|
||||
"symbol": tkn.tokenSymbol(),
|
||||
"decimals": (if decimals == 0: "" else: $decimals)
|
||||
})
|
||||
self.getTokenDetails("tokenDetailsResolved", address)
|
||||
|
||||
proc tokenDetailsWereResolved*(self: TokenList, tokenDetails: string) {.signal.}
|
||||
|
||||
|
|
|
@ -10,13 +10,11 @@ import app/onboarding/core as onboarding
|
|||
import app/login/core as login
|
||||
import app/provider/core as provider
|
||||
import status/signals/core as signals
|
||||
import status/tasks/task_manager
|
||||
import status/libstatus/types
|
||||
import status/libstatus/accounts/constants
|
||||
import status_go
|
||||
import status/status as statuslib
|
||||
import ./eventemitter
|
||||
import chronos, task_runner
|
||||
|
||||
var signalsQObjPointer: pointer
|
||||
|
||||
|
@ -30,9 +28,7 @@ proc mainProc() =
|
|||
else:
|
||||
"/../fleets.json"
|
||||
|
||||
let taskManager = newTaskManager()
|
||||
taskManager.init()
|
||||
let status = statuslib.newStatusInstance(taskManager, readFile(joinPath(getAppDir(), fleets)))
|
||||
let status = statuslib.newStatusInstance(readFile(joinPath(getAppDir(), fleets)))
|
||||
status.initNode()
|
||||
|
||||
enableHDPI()
|
||||
|
@ -157,7 +153,7 @@ proc mainProc() =
|
|||
profile.delete()
|
||||
utilsController.delete()
|
||||
browserController.delete()
|
||||
taskManager.teardown()
|
||||
status.tasks.teardown()
|
||||
|
||||
|
||||
# Initialize only controllers whose init functions
|
||||
|
@ -205,4 +201,3 @@ proc mainProc() =
|
|||
|
||||
when isMainModule:
|
||||
mainProc()
|
||||
GC_fullcollect()
|
||||
|
|
|
@ -4,9 +4,9 @@ import libstatus/settings as libstatus_settings
|
|||
import libstatus/types as libstatus_types
|
||||
import chat, accounts, wallet, node, network, mailservers, messages, contacts, profile, stickers, permissions, fleet
|
||||
import ../eventemitter
|
||||
import tasks/task_manager
|
||||
import ./tasks/task_runner_impl
|
||||
|
||||
export chat, accounts, node, mailservers, messages, contacts, profile, network, permissions, fleet
|
||||
export chat, accounts, node, mailservers, messages, contacts, profile, network, permissions, fleet, task_runner_impl
|
||||
|
||||
type Status* = ref object
|
||||
events*: EventEmitter
|
||||
|
@ -22,11 +22,11 @@ type Status* = ref object
|
|||
network*: NetworkModel
|
||||
stickers*: StickersModel
|
||||
permissions*: PermissionsModel
|
||||
taskManager*: TaskManager
|
||||
tasks*: TaskRunner
|
||||
|
||||
proc newStatusInstance*(taskManager: TaskManager, fleetConfig: string): Status =
|
||||
proc newStatusInstance*(fleetConfig: string): Status =
|
||||
result = Status()
|
||||
result.taskManager = taskManager
|
||||
result.tasks = newTaskRunner()
|
||||
result.events = createEventEmitter()
|
||||
result.fleet = fleet.newFleetModel(result.events, fleetConfig)
|
||||
result.chat = chat.newChatModel(result.events)
|
||||
|
@ -43,6 +43,7 @@ proc newStatusInstance*(taskManager: TaskManager, fleetConfig: string): Status =
|
|||
result.permissions = permissions.newPermissionsModel(result.events)
|
||||
|
||||
proc initNode*(self: Status) =
|
||||
self.tasks.init()
|
||||
libstatus_accounts.initNode()
|
||||
|
||||
proc startMessenger*(self: Status) =
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
import
|
||||
import # vendor libs
|
||||
json_serialization, NimQml, task_runner
|
||||
|
||||
type
|
||||
BaseTasks* = ref object of RootObj
|
||||
chanSendToPool*: AsyncChannel[ThreadSafeString]
|
||||
BaseTask* = ref object of RootObj
|
||||
vptr*: ByteAddress
|
||||
slot*: string
|
||||
Task* = proc(arg: string): void {.gcsafe, nimcall.}
|
||||
TaskArg* = ref object of RootObj
|
||||
tptr*: ByteAddress
|
||||
|
||||
proc start*[T: BaseTask](self: BaseTasks, task: T) =
|
||||
let payload = task.toJson(typeAnnotations = true)
|
||||
self.chanSendToPool.sendSync(payload.safe)
|
||||
proc decode*[T](arg: string): T =
|
||||
Json.decode(arg, T, allowUnknownFields = true)
|
||||
|
||||
proc finish*[T](task: BaseTask, payload: T) =
|
||||
let resultPayload = Json.encode(payload)
|
||||
signal_handler(cast[pointer](task.vptr), resultPayload, task.slot)
|
||||
proc encode*[T](arg: T): string =
|
||||
arg.toJson(typeAnnotations = true)
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import # vendor libs
|
||||
NimQml, json_serialization
|
||||
|
||||
import # status-desktop libs
|
||||
./common
|
||||
|
||||
type
|
||||
QObjectTaskArg* = ref object of TaskArg
|
||||
vptr*: ByteAddress
|
||||
slot*: string
|
||||
|
||||
proc finish*[T](arg: QObjectTaskArg, payload: T) =
|
||||
signal_handler(cast[pointer](arg.vptr), Json.encode(payload), arg.slot)
|
||||
|
||||
proc finish*(arg: QObjectTaskArg, payload: string) =
|
||||
signal_handler(cast[pointer](arg.vptr), payload, arg.slot)
|
|
@ -1,50 +0,0 @@
|
|||
import # nim libs
|
||||
tables
|
||||
|
||||
import # vendor libs
|
||||
chronos, NimQml, json, json_serialization, task_runner
|
||||
|
||||
import # status-desktop libs
|
||||
./common, ../libstatus/types, ../stickers
|
||||
|
||||
type
|
||||
StickerPackPurchaseGasEstimate* = ref object of BaseTask
|
||||
packId*: int
|
||||
address*: string
|
||||
price*: string
|
||||
uuid*: string
|
||||
ObtainAvailableStickerPacks* = ref object of BaseTask
|
||||
StickersTasks* = ref object of BaseTasks
|
||||
|
||||
proc newStickersTasks*(chanSendToPool: AsyncChannel[ThreadSafeString]): StickersTasks =
|
||||
new(result)
|
||||
result.chanSendToPool = chanSendToPool
|
||||
|
||||
proc run*(task: StickerPackPurchaseGasEstimate) =
|
||||
var success: bool
|
||||
var estimate = estimateGas(
|
||||
task.packId,
|
||||
task.address,
|
||||
task.price,
|
||||
success
|
||||
)
|
||||
if not success:
|
||||
estimate = 325000
|
||||
let result: tuple[estimate: int, uuid: string] = (estimate, task.uuid)
|
||||
task.finish(result)
|
||||
|
||||
proc run*(task: ObtainAvailableStickerPacks) =
|
||||
var success: bool
|
||||
let availableStickerPacks = getAvailableStickerPacks()
|
||||
var packs: seq[StickerPack] = @[]
|
||||
for packId, stickerPack in availableStickerPacks.pairs:
|
||||
packs.add(stickerPack)
|
||||
task.finish(%*(packs))
|
||||
|
||||
proc stickerPackPurchaseGasEstimate*(self: StickersTasks, vptr: pointer, slot: string, packId: int, address: string, price: string, uuid: string) =
|
||||
let task = StickerPackPurchaseGasEstimate(vptr: cast[ByteAddress](vptr), slot: slot, packId: packId, address: address, price: price, uuid: uuid)
|
||||
self.start(task)
|
||||
|
||||
proc obtainAvailableStickerPacks*(self: StickersTasks, vptr: pointer, slot: string) =
|
||||
let task = ObtainAvailableStickerPacks(vptr: cast[ByteAddress](vptr), slot: slot)
|
||||
self.start(task)
|
|
@ -1,27 +0,0 @@
|
|||
import # vendor libs
|
||||
chronicles, task_runner
|
||||
|
||||
import # status-desktop libs
|
||||
./threadpool
|
||||
|
||||
export threadpool
|
||||
|
||||
logScope:
|
||||
topics = "task-manager"
|
||||
|
||||
type
|
||||
TaskManager* = ref object
|
||||
threadPool*: ThreadPool
|
||||
|
||||
proc newTaskManager*(): TaskManager =
|
||||
new(result)
|
||||
result.threadPool = newThreadPool()
|
||||
|
||||
proc init*(self: TaskManager) =
|
||||
self.threadPool.init()
|
||||
|
||||
proc teardown*(self: TaskManager) =
|
||||
self.threadPool.teardown()
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import # vendor libs
|
||||
chronicles, task_runner
|
||||
|
||||
import # status-desktop libs
|
||||
./threadpool
|
||||
|
||||
export task_runner, threadpool
|
||||
|
||||
logScope:
|
||||
topics = "task-runner"
|
||||
|
||||
type
|
||||
TaskRunner* = ref object
|
||||
threadpool*: ThreadPool
|
||||
|
||||
proc newTaskRunner*(): TaskRunner =
|
||||
new(result)
|
||||
result.threadpool = newThreadPool()
|
||||
|
||||
proc init*(self: TaskRunner) =
|
||||
self.threadpool.init()
|
||||
|
||||
proc teardown*(self: TaskRunner) =
|
||||
self.threadpool.teardown()
|
|
@ -1,26 +1,28 @@
|
|||
import
|
||||
chronicles, chronos, json, json_serialization, NimQml, sequtils, tables,
|
||||
task_runner
|
||||
import # std libs
|
||||
json, sequtils, tables
|
||||
|
||||
import # vendor libs
|
||||
chronicles, chronos, json_serialization, NimQml, task_runner
|
||||
|
||||
import # status-desktop libs
|
||||
./common
|
||||
|
||||
import
|
||||
./common, ./stickers
|
||||
export
|
||||
stickers
|
||||
chronos, common, json_serialization
|
||||
|
||||
logScope:
|
||||
topics = "task-threadpool"
|
||||
|
||||
type
|
||||
ThreadPool* = ref object
|
||||
chanRecvFromPool*: AsyncChannel[ThreadSafeString]
|
||||
chanSendToPool*: AsyncChannel[ThreadSafeString]
|
||||
chanRecvFromPool: AsyncChannel[ThreadSafeString]
|
||||
chanSendToPool: AsyncChannel[ThreadSafeString]
|
||||
thread: Thread[PoolThreadArg]
|
||||
size: int
|
||||
stickers*: StickersTasks
|
||||
PoolThreadArg* = object
|
||||
chanSendToMain*: AsyncChannel[ThreadSafeString]
|
||||
chanRecvFromMain*: AsyncChannel[ThreadSafeString]
|
||||
size*: int
|
||||
PoolThreadArg = object
|
||||
chanSendToMain: AsyncChannel[ThreadSafeString]
|
||||
chanRecvFromMain: AsyncChannel[ThreadSafeString]
|
||||
size: int
|
||||
TaskThreadArg = object
|
||||
id: int
|
||||
chanRecvFromPool: AsyncChannel[ThreadSafeString]
|
||||
|
@ -29,7 +31,6 @@ type
|
|||
id: int
|
||||
notice: string
|
||||
|
||||
|
||||
# forward declarations
|
||||
proc poolThread(arg: PoolThreadArg) {.thread.}
|
||||
|
||||
|
@ -41,7 +42,6 @@ proc newThreadPool*(size: int = MaxThreadPoolSize): ThreadPool =
|
|||
result.chanSendToPool = newAsyncChannel[ThreadSafeString](-1)
|
||||
result.thread = Thread[PoolThreadArg]()
|
||||
result.size = size
|
||||
result.stickers = newStickersTasks(result.chanSendToPool)
|
||||
|
||||
proc init*(self: ThreadPool) =
|
||||
self.chanRecvFromPool.open()
|
||||
|
@ -52,64 +52,60 @@ proc init*(self: ThreadPool) =
|
|||
size: self.size
|
||||
)
|
||||
createThread(self.thread, poolThread, arg)
|
||||
|
||||
# block until we receive "ready"
|
||||
let received = $(self.chanRecvFromPool.recvSync())
|
||||
discard $(self.chanRecvFromPool.recvSync())
|
||||
|
||||
proc teardown*(self: ThreadPool) =
|
||||
self.chanSendToPool.sendSync("shutdown".safe)
|
||||
self.chanRecvFromPool.close()
|
||||
self.chanSendToPool.close()
|
||||
debug "[threadpool] waiting for the control thread to stop"
|
||||
joinThread(self.thread)
|
||||
|
||||
proc task(arg: TaskThreadArg) {.async.} =
|
||||
proc start*[T: TaskArg](self: Threadpool, arg: T) =
|
||||
self.chanSendToPool.sendSync(arg.encode.safe)
|
||||
|
||||
proc runner(arg: TaskThreadArg) {.async.} =
|
||||
arg.chanRecvFromPool.open()
|
||||
arg.chanSendToPool.open()
|
||||
|
||||
let noticeToPool = ThreadNotification(id: arg.id, notice: "ready")
|
||||
info "[threadpool task thread] sending 'ready'", threadid=arg.id
|
||||
await arg.chanSendToPool.send(noticeToPool.toJson(typeAnnotations = true).safe)
|
||||
debug "[threadpool task thread] sending 'ready'", threadid=arg.id
|
||||
await arg.chanSendToPool.send(noticeToPool.encode.safe)
|
||||
|
||||
while true:
|
||||
info "[threadpool task thread] waiting for message"
|
||||
debug "[threadpool task thread] waiting for message"
|
||||
let received = $(await arg.chanRecvFromPool.recv())
|
||||
|
||||
if received == "shutdown":
|
||||
info "[threadpool task thread] received 'shutdown'"
|
||||
info "[threadpool task thread] breaking while loop"
|
||||
debug "[threadpool task thread] received 'shutdown'"
|
||||
break
|
||||
|
||||
let
|
||||
jsonNode = parseJson(received)
|
||||
messageType = jsonNode{"$type"}.getStr
|
||||
|
||||
info "[threadpool task thread] received task", messageType=messageType
|
||||
info "[threadpool task thread] initiating task", messageType=messageType,
|
||||
parsed = parseJson(received)
|
||||
messageType = parsed{"$type"}.getStr
|
||||
debug "[threadpool task thread] initiating task", messageType=messageType,
|
||||
threadid=arg.id
|
||||
|
||||
try:
|
||||
case messageType
|
||||
of "StickerPackPurchaseGasEstimate:ObjectType":
|
||||
let decoded = Json.decode(received, StickerPackPurchaseGasEstimate, allowUnknownFields = true)
|
||||
decoded.run()
|
||||
of "ObtainAvailableStickerPacks:ObjectType":
|
||||
let decoded = Json.decode(received, ObtainAvailableStickerPacks, allowUnknownFields = true)
|
||||
decoded.run()
|
||||
else:
|
||||
error "[threadpool task thread] unknown message", message=received
|
||||
let task = cast[Task](parsed{"tptr"}.getInt)
|
||||
try:
|
||||
task(received)
|
||||
except Exception as e:
|
||||
error "[threadpool task thread] exception", error=e.msg
|
||||
except Exception as e:
|
||||
error "[threadpool task thread] unknown message", message=received
|
||||
|
||||
let noticeToPool = ThreadNotification(id: arg.id, notice: "done")
|
||||
info "[threadpool task thread] sending 'done' notice to pool",
|
||||
debug "[threadpool task thread] sending 'done' notice to pool",
|
||||
threadid=arg.id
|
||||
await arg.chanSendToPool.send(noticeToPool.toJson(typeAnnotations = true).safe)
|
||||
await arg.chanSendToPool.send(noticeToPool.encode.safe)
|
||||
|
||||
arg.chanRecvFromPool.close()
|
||||
arg.chanSendToPool.close()
|
||||
|
||||
proc taskThread(arg: TaskThreadArg) {.thread.} =
|
||||
waitFor task(arg)
|
||||
waitFor runner(arg)
|
||||
|
||||
proc pool(arg: PoolThreadArg) {.async.} =
|
||||
let
|
||||
|
@ -124,14 +120,14 @@ proc pool(arg: PoolThreadArg) {.async.} =
|
|||
chanSendToMain.open()
|
||||
chanRecvFromMainOrTask.open()
|
||||
|
||||
info "[threadpool] sending 'ready' to main thread"
|
||||
debug "[threadpool] sending 'ready' to main thread"
|
||||
await chanSendToMain.send("ready".safe)
|
||||
|
||||
for i in 0..<arg.size:
|
||||
let id = i + 1
|
||||
let chanSendToTask = newAsyncChannel[ThreadSafeString](-1)
|
||||
chanSendToTask.open()
|
||||
info "[threadpool] adding to threadsIdle", threadid=id
|
||||
debug "[threadpool] adding to threadsIdle", threadid=id
|
||||
threadsIdle[i].id = id
|
||||
createThread(
|
||||
threadsIdle[i].thr,
|
||||
|
@ -153,55 +149,53 @@ proc pool(arg: PoolThreadArg) {.async.} =
|
|||
# push thread into threadsIdle
|
||||
|
||||
while true:
|
||||
info "[threadpool] waiting for message"
|
||||
debug "[threadpool] waiting for message"
|
||||
var task = $(await chanRecvFromMainOrTask.recv())
|
||||
info "[threadpool] received message", msg=task
|
||||
|
||||
if task == "shutdown":
|
||||
info "[threadpool] sending 'shutdown' to all task threads"
|
||||
debug "[threadpool] sending 'shutdown' to all task threads"
|
||||
for tpl in threadsIdle:
|
||||
await tpl.chanSendToTask.send("shutdown".safe)
|
||||
for tpl in threadsBusy.values:
|
||||
await tpl.chanSendToTask.send("shutdown".safe)
|
||||
info "[threadpool] breaking while loop"
|
||||
break
|
||||
|
||||
let
|
||||
jsonNode = parseJson(task)
|
||||
messageType = jsonNode{"$type"}.getStr
|
||||
info "[threadpool] determined message type", messageType=messageType
|
||||
debug "[threadpool] determined message type", messageType=messageType
|
||||
|
||||
case messageType
|
||||
of "ThreadNotification":
|
||||
try:
|
||||
let notification = Json.decode(task, ThreadNotification, allowUnknownFields = true)
|
||||
info "[threadpool] received notification",
|
||||
let notification = decode[ThreadNotification](task)
|
||||
debug "[threadpool] received notification",
|
||||
notice=notification.notice, threadid=notification.id
|
||||
|
||||
if notification.notice == "ready":
|
||||
info "[threadpool] received 'ready' from a task thread"
|
||||
debug "[threadpool] received 'ready' from a task thread"
|
||||
allReady = allReady + 1
|
||||
|
||||
elif notification.notice == "done":
|
||||
let tpl = threadsBusy[notification.id]
|
||||
info "[threadpool] adding to threadsIdle",
|
||||
debug "[threadpool] adding to threadsIdle",
|
||||
newlength=(threadsIdle.len + 1)
|
||||
threadsIdle.add (notification.id, tpl.thr, tpl.chanSendToTask)
|
||||
info "[threadpool] removing from threadsBusy",
|
||||
debug "[threadpool] removing from threadsBusy",
|
||||
newlength=(threadsBusy.len - 1), threadid=notification.id
|
||||
threadsBusy.del notification.id
|
||||
|
||||
if taskQueue.len > 0:
|
||||
info "[threadpool] removing from taskQueue",
|
||||
debug "[threadpool] removing from taskQueue",
|
||||
newlength=(taskQueue.len - 1)
|
||||
task = taskQueue[0]
|
||||
taskQueue.delete 0, 0
|
||||
|
||||
info "[threadpool] removing from threadsIdle",
|
||||
debug "[threadpool] removing from threadsIdle",
|
||||
newlength=(threadsIdle.len - 1)
|
||||
let tpl = threadsIdle[0]
|
||||
threadsIdle.delete 0, 0
|
||||
info "[threadpool] adding to threadsBusy",
|
||||
debug "[threadpool] adding to threadsBusy",
|
||||
newlength=(threadsBusy.len + 1), threadid=tpl.id
|
||||
threadsBusy.add tpl.id, (tpl.thr, tpl.chanSendToTask)
|
||||
await tpl.chanSendToTask.send(task.safe)
|
||||
|
@ -214,7 +208,7 @@ proc pool(arg: PoolThreadArg) {.async.} =
|
|||
else: # must be a request to do task work
|
||||
if allReady < arg.size or threadsBusy.len == arg.size:
|
||||
# add to queue
|
||||
info "[threadpool] adding to taskQueue",
|
||||
debug "[threadpool] adding to taskQueue",
|
||||
newlength=(taskQueue.len + 1)
|
||||
taskQueue.add task
|
||||
|
||||
|
@ -223,24 +217,23 @@ proc pool(arg: PoolThreadArg) {.async.} =
|
|||
# check if we have tasks waiting on queue
|
||||
if taskQueue.len > 0:
|
||||
# remove first element from the task queue
|
||||
info "[threadpool] adding to taskQueue",
|
||||
debug "[threadpool] adding to taskQueue",
|
||||
newlength=(taskQueue.len + 1)
|
||||
taskQueue.add task
|
||||
info "[threadpool] removing from taskQueue",
|
||||
debug "[threadpool] removing from taskQueue",
|
||||
newlength=(taskQueue.len - 1)
|
||||
task = taskQueue[0]
|
||||
taskQueue.delete 0, 0
|
||||
|
||||
info "[threadpool] removing from threadsIdle",
|
||||
debug "[threadpool] removing from threadsIdle",
|
||||
newlength=(threadsIdle.len - 1)
|
||||
let tpl = threadsIdle[0]
|
||||
threadsIdle.delete 0, 0
|
||||
info "[threadpool] adding to threadsBusy",
|
||||
debug "[threadpool] adding to threadsBusy",
|
||||
newlength=(threadsBusy.len + 1), threadid=tpl.id
|
||||
threadsBusy.add tpl.id, (tpl.thr, tpl.chanSendToTask)
|
||||
await tpl.chanSendToTask.send(task.safe)
|
||||
|
||||
|
||||
var allTaskThreads: seq[Thread[TaskThreadArg]] = @[]
|
||||
|
||||
for tpl in threadsIdle:
|
||||
|
@ -253,6 +246,7 @@ proc pool(arg: PoolThreadArg) {.async.} =
|
|||
chanSendToMain.close()
|
||||
chanRecvFromMainOrTask.close()
|
||||
|
||||
debug "[threadpool] waiting for all task threads to stop"
|
||||
joinThreads(allTaskThreads)
|
||||
|
||||
proc poolThread(arg: PoolThreadArg) {.thread.} =
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
import nimqml
|
||||
import threadpool
|
||||
import stew/faux_closures
|
||||
|
||||
export fauxClosure
|
||||
|
||||
template spawnAndSend*(view: untyped, signalName: string, exprBlock: untyped) =
|
||||
let viewPtr = cast[pointer](view.vptr)
|
||||
proc backgroundTask() {.fauxClosure.} =
|
||||
let data = exprBlock
|
||||
signal_handler(viewPtr, data, signalName)
|
||||
spawn backgroundTask()
|
|
@ -63,7 +63,7 @@ proc initEvents*(self: WalletModel) =
|
|||
proc delete*(self: WalletModel) =
|
||||
discard
|
||||
|
||||
proc buildTokenTransaction(self: WalletModel, source, to, assetAddress: Address, value: float, transfer: var Transfer, contract: var Erc20Contract, gas = "", gasPrice = ""): EthSend =
|
||||
proc buildTokenTransaction(source, to, assetAddress: Address, value: float, transfer: var Transfer, contract: var Erc20Contract, gas = "", gasPrice = ""): EthSend =
|
||||
contract = getErc20Contract(assetAddress)
|
||||
if contract == nil:
|
||||
raise newException(ValueError, fmt"Could not find ERC-20 contract with address '{assetAddress}' for the current network")
|
||||
|
@ -114,7 +114,7 @@ proc estimateTokenGas*(self: WalletModel, source, to, assetAddress, value: strin
|
|||
var
|
||||
transfer: Transfer
|
||||
contract: Erc20Contract
|
||||
tx = self.buildTokenTransaction(
|
||||
tx = buildTokenTransaction(
|
||||
parseAddress(source),
|
||||
parseAddress(to),
|
||||
parseAddress(assetAddress),
|
||||
|
@ -125,7 +125,7 @@ proc estimateTokenGas*(self: WalletModel, source, to, assetAddress, value: strin
|
|||
|
||||
result = contract.methods["transfer"].estimateGas(tx, transfer, success)
|
||||
|
||||
proc sendTransaction*(self: WalletModel, source, to, value, gas, gasPrice, password: string, success: var bool, data = ""): string =
|
||||
proc sendTransaction*(source, to, value, gas, gasPrice, password: string, success: var bool, data = ""): string =
|
||||
var tx = transactions.buildTransaction(
|
||||
parseAddress(source),
|
||||
eth2Wei(parseFloat(value), 18), gas, gasPrice, data
|
||||
|
@ -138,11 +138,11 @@ proc sendTransaction*(self: WalletModel, source, to, value, gas, gasPrice, passw
|
|||
if success:
|
||||
trackPendingTransaction(result, $source, $to, PendingTransactionType.WalletTransfer, "")
|
||||
|
||||
proc sendTokenTransaction*(self: WalletModel, source, to, assetAddress, value, gas, gasPrice, password: string, success: var bool): string =
|
||||
proc sendTokenTransaction*(source, to, assetAddress, value, gas, gasPrice, password: string, success: var bool): string =
|
||||
var
|
||||
transfer: Transfer
|
||||
contract: Erc20Contract
|
||||
tx = self.buildTokenTransaction(
|
||||
tx = buildTokenTransaction(
|
||||
parseAddress(source),
|
||||
parseAddress(to),
|
||||
parseAddress(assetAddress),
|
||||
|
@ -333,22 +333,7 @@ proc getTransfersByAddress*(self: WalletModel, address: string): seq[Transaction
|
|||
proc validateMnemonic*(self: WalletModel, mnemonic: string): string =
|
||||
result = status_wallet.validateMnemonic(mnemonic).parseJSON()["error"].getStr
|
||||
|
||||
proc getGasPricePredictions*(self: WalletModel): GasPricePrediction =
|
||||
if status_settings.getCurrentNetwork() != Network.Mainnet:
|
||||
# TODO: what about other chains like xdai?
|
||||
return GasPricePrediction(safeLow: 1.0, standard: 2.0, fast: 3.0, fastest: 4.0)
|
||||
try:
|
||||
let url: string = fmt"https://etherchain.org/api/gasPriceOracle"
|
||||
let secureSSLContext = newContext()
|
||||
let client = newHttpClient(sslContext = secureSSLContext)
|
||||
client.headers = newHttpHeaders({ "Content-Type": "application/json" })
|
||||
let response = client.request(url)
|
||||
result = Json.decode(response.body, GasPricePrediction)
|
||||
except Exception as e:
|
||||
echo "error getting gas price predictions"
|
||||
echo e.msg
|
||||
|
||||
proc getGasPricePredictions2*(): GasPricePrediction =
|
||||
proc getGasPricePredictions*(): GasPricePrediction =
|
||||
if status_settings.getCurrentNetwork() != Network.Mainnet:
|
||||
# TODO: what about other chains like xdai?
|
||||
return GasPricePrediction(safeLow: 1.0, standard: 2.0, fast: 3.0, fastest: 4.0)
|
||||
|
|
Loading…
Reference in New Issue