diff --git a/src/app/chat/core.nim b/src/app/chat/core.nim index 2bcc98f101..ee2e904bf9 100644 --- a/src/app/chat/core.nim +++ b/src/app/chat/core.nim @@ -47,4 +47,16 @@ proc init*(self: ChatController) = for sticker in recentStickers: self.view.stickers.addRecentStickerToList(sticker) self.status.stickers.addStickerToRecent(sticker) - self.view.stickers.obtainAvailableStickerPacks() + + if self.status.network.isConnected: + self.view.stickers.obtainAvailableStickerPacks() + else: + self.view.stickers.populateOfflineStickerPacks() + + self.status.events.on("network:disconnected") do(e: Args): + self.view.stickers.clearStickerPacks() + self.view.stickers.populateOfflineStickerPacks() + + self.status.events.on("network:connected") do(e: Args): + self.view.stickers.clearStickerPacks() + self.view.stickers.obtainAvailableStickerPacks() diff --git a/src/app/chat/event_handling.nim b/src/app/chat/event_handling.nim index 040fcc74a5..0ccb76f263 100644 --- a/src/app/chat/event_handling.nim +++ b/src/app/chat/event_handling.nim @@ -74,10 +74,10 @@ proc handleChatEvents(self: ChatController) = var msg = MessageSentArgs(e) self.view.markMessageAsSent(msg.chatId, msg.id) - self.status.events.on("chat:disconnected") do(e: Args): + self.status.events.on("network:disconnected") do(e: Args): self.view.setConnected(false) - self.status.events.on("chat:connected") do(e: Args): + self.status.events.on("network:connected") do(e: Args): self.view.setConnected(true) self.status.events.on(PendingTransactionType.BuyStickerPack.confirmed) do(e: Args): diff --git a/src/app/chat/view.nim b/src/app/chat/view.nim index edc5363533..05424573ad 100644 --- a/src/app/chat/view.nim +++ b/src/app/chat/view.nim @@ -459,7 +459,7 @@ QtObject: self.ensWasResolved(pubKey) proc isConnected*(self: ChatsView): bool {.slot.} = - result = self.connected + result = self.status.network.isConnected proc onlineStatusChanged(self: ChatsView, connected: bool) {.signal.} diff --git a/src/app/chat/views/sticker_pack_list.nim b/src/app/chat/views/sticker_pack_list.nim index 58b652bd22..476c5d655a 100644 --- a/src/app/chat/views/sticker_pack_list.nim +++ b/src/app/chat/views/sticker_pack_list.nim @@ -27,6 +27,11 @@ QtObject: proc delete(self: StickerPackList) = self.QAbstractListModel.delete + proc clear*(self: StickerPackList) = + self.beginResetModel() + self.packs = @[] + self.endResetModel() + proc newStickerPackList*(): StickerPackList = new(result, delete) result.packs = @[] diff --git a/src/app/chat/views/stickers.nim b/src/app/chat/views/stickers.nim index 36956f24f4..b1d56e1995 100644 --- a/src/app/chat/views/stickers.nim +++ b/src/app/chat/views/stickers.nim @@ -44,11 +44,21 @@ QtObject: proc transactionCompleted*(self: StickersView, success: bool, txHash: string, revertReason: string = "") {.signal.} - proc estimate*(self: StickersView, packId: int, address: string, price: string): int {.slot.} = - var success: bool - result = self.status.stickers.estimateGas(packId, address, price, success) - if not success: - result = 325000 + proc estimate*(self: StickersView, packId: int, address: string, price: string, uuid: string) {.slot.} = + let status_stickers = self.status.stickers + spawnAndSend(self, "setGasEstimate") do: + var success: bool + var estimate = status_stickers.estimateGas(packId, address, price, success) + if not success: + estimate = 325000 + let result: tuple[estimate: int, uuid: string] = (estimate, uuid) + Json.encode(result) + + proc gasEstimateReturned*(self: StickersView, estimate: int, uuid: string) {.signal.} + + proc setGasEstimate*(self: StickersView, estimateJson: string) {.slot.} = + let estimateResult = Json.decode(estimateJson, tuple[estimate: int, uuid: string]) + self.gasEstimateReturned(estimateResult.estimate, estimateResult.uuid) proc buy*(self: StickersView, packId: int, address: string, price: string, gas: string, gasPrice: string, password: string): string {.slot.} = var success: bool @@ -74,6 +84,14 @@ QtObject: proc recentStickersUpdated*(self: StickersView) {.signal.} + proc clearStickerPacks*(self: StickersView) = + self.stickerPacks.clear() + + proc populateOfflineStickerPacks*(self: StickersView) = + let installedStickerPacks = self.status.stickers.getInstalledStickerPacks() + for stickerPack in installedStickerPacks.values: + self.addStickerPackToList(stickerPack, isInstalled = true, isBought = true, isPending = false) + proc setAvailableStickerPacks*(self: StickersView, availableStickersJSON: string) {.slot.} = let accounts = status_wallet.getWalletAccounts() # TODO: make generic diff --git a/src/app/node/core.nim b/src/app/node/core.nim index d0c5d157af..505d19e411 100644 --- a/src/app/node/core.nim +++ b/src/app/node/core.nim @@ -12,12 +12,14 @@ type NodeController* = ref object status*: Status view*: NodeView variant*: QVariant + networkAccessMananger*: QNetworkAccessManager -proc newController*(status: Status): NodeController = +proc newController*(status: Status, nam: QNetworkAccessManager): NodeController = result = NodeController() result.status = status result.view = newNodeView(status) result.variant = newQVariant(result.view) + result.networkAccessMananger = nam proc delete*(self: NodeController) = delete self.variant diff --git a/src/app/utilsView/core.nim b/src/app/utilsView/core.nim index c0412a21d1..e9400419ff 100644 --- a/src/app/utilsView/core.nim +++ b/src/app/utilsView/core.nim @@ -3,6 +3,7 @@ import ../../status/signals/types import ../../status/[status, node, network] import ../../status/libstatus/types as status_types import view +import ../../eventemitter logScope: topics = "utils" diff --git a/src/nim_status_client.nim b/src/nim_status_client.nim index 9c48bdd250..55142be940 100644 --- a/src/nim_status_client.nim +++ b/src/nim_status_client.nim @@ -67,8 +67,23 @@ proc mainProc() = i18nPath = joinPath(getAppDir(), "../i18n") let networkAccessFactory = newQNetworkAccessManagerFactory(TMPDIR & "netcache") + let engine = newQQmlApplicationEngine() engine.setNetworkAccessManagerFactory(networkAccessFactory) + + let netAccMgr = newQNetworkAccessManager(engine.getNetworkAccessManager()) + + status.events.on("network:connected") do(e: Args): + # This is a workaround for Qt bug https://bugreports.qt.io/browse/QTBUG-55180 + # that was apparently reintroduced in 5.14.1 Unfortunately, the only workaround + # that could be found uses obsolete properties and methods + # (https://doc.qt.io/qt-5/qnetworkaccessmanager-obsolete.html), so this will + # need to be something we keep in mind when upgrading to Qt 6. + # The workaround is to manually set the NetworkAccessible property of the + # QNetworkAccessManager once peers have dropped (network connection is lost). + netAccMgr.clearConnectionCache() + netAccMgr.setNetworkAccessible(NetworkAccessibility.Accessible) + let signalController = signals.newController(status) # We need this global variable in order to be able to access the application @@ -81,7 +96,7 @@ proc mainProc() = var chat = chat.newController(status) engine.setRootContextProperty("chatsModel", chat.variant) - var node = node.newController(status) + var node = node.newController(status, netAccMgr) engine.setRootContextProperty("nodeModel", node.variant) var utilsController = utilsView.newController(status) diff --git a/src/status/network.nim b/src/status/network.nim index 74001649a8..9c4851f7a1 100644 --- a/src/status/network.nim +++ b/src/status/network.nim @@ -8,19 +8,25 @@ type NetworkModel* = ref object peers*: seq[string] events*: EventEmitter + connected*: bool proc newNetworkModel*(events: EventEmitter): NetworkModel = result = NetworkModel() result.events = events result.peers = @[] + result.connected = false proc peerSummaryChange*(self: NetworkModel, peers: seq[string]) = - if peers.len == 0: - self.events.emit("chat:disconnected", Args()) + if peers.len == 0 and self.connected: + self.connected = false + self.events.emit("network:disconnected", Args()) - if peers.len > 0: - self.events.emit("chat:connected", Args()) + if peers.len > 0 and not self.connected: + self.connected = true + self.events.emit("network:connected", Args()) self.peers = peers proc peerCount*(self: NetworkModel): int = self.peers.len + +proc isConnected*(self: NetworkModel): bool = self.connected diff --git a/src/status/stickers.nim b/src/status/stickers.nim index 09db15c034..5cb032f15c 100644 --- a/src/status/stickers.nim +++ b/src/status/stickers.nim @@ -58,10 +58,11 @@ proc estimateGas*(self: StickersModel, packId: int, address: string, price: stri tx = self.buildTransaction( packId.u256, parseAddress(address), - eth2Wei(parseFloat(price), 18), # SNT + eth2Wei(parseFloat(price), sntContract.decimals), approveAndCall, sntContract ) + let response = sntContract.methods["approveAndCall"].estimateGas(tx, approveAndCall, success) if success: result = fromHex[int](response) diff --git a/src/status/wallet.nim b/src/status/wallet.nim index c1c5aa9de0..8381931119 100644 --- a/src/status/wallet.nim +++ b/src/status/wallet.nim @@ -99,8 +99,10 @@ proc confirmTransactionStatus(self: WalletModel, pendingTransactions: JsonNode, self.events.emit(parseEnum[PendingTransactionType](trx["type"].getStr).confirmed, ev) proc checkPendingTransactions*(self: WalletModel) = - let latestBlock = parseInt($fromHex(Stuint[256], getBlockByNumber("latest").parseJson()["result"]["number"].getStr)) - self.confirmTransactionStatus(status_wallet.getPendingTransactions().parseJson["result"], latestBlock) + let response = getBlockByNumber("latest").parseJson() + if response.hasKey("result"): + let latestBlock = parseInt($fromHex(Stuint[256], response["result"]["number"].getStr)) + self.confirmTransactionStatus(status_wallet.getPendingTransactions().parseJson["result"], latestBlock) proc checkPendingTransactions*(self: WalletModel, address: string, blockNumber: int) = self.confirmTransactionStatus(status_wallet.getPendingOutboundTransactionsByAddress(address).parseJson["result"], blockNumber) diff --git a/ui/shared/GasSelector.qml b/ui/shared/GasSelector.qml index 5eb6b80f10..563c267f33 100644 --- a/ui/shared/GasSelector.qml +++ b/ui/shared/GasSelector.qml @@ -27,6 +27,7 @@ Item { //% "Please enter an amount" property string noInputErrorMessage: qsTrId("please-enter-an-amount") property bool isValid: true + readonly property string uuid: Utils.uuid() function defaultGasPrice() { return ((50 * (root.fastestGasPrice - root.slowestGasPrice) / 100) + root.slowestGasPrice) diff --git a/ui/shared/ImageLoader.qml b/ui/shared/ImageLoader.qml index 3f2ec1772b..f5db07b38e 100644 --- a/ui/shared/ImageLoader.qml +++ b/ui/shared/ImageLoader.qml @@ -69,13 +69,29 @@ Rectangle { } ] + Connections { + target: chatsModel + onOnlineStatusChanged: { + if (connected && root.state !== "ready" && + root.visible && + root.source && + root.source.startsWith("http")) { + root.reload() + } + } + } + function reload() { // From the documentation (https://doc.qt.io/qt-5/qml-qtquick-image.html#sourceSize-prop) // Note: Changing this property dynamically causes the image source to // be reloaded, potentially even from the network, if it is not in the // disk cache. + const oldSource = image.source + image.cache = false image.sourceSize.width += 1 image.sourceSize.width -= 1 + image.cache = true + } Component { diff --git a/ui/shared/status/StatusStickerButton.qml b/ui/shared/status/StatusStickerButton.qml index bcdea7d827..80900ecd78 100644 --- a/ui/shared/status/StatusStickerButton.qml +++ b/ui/shared/status/StatusStickerButton.qml @@ -99,7 +99,7 @@ Item { bgColor: root.style === StatusStickerButton.StyleType.Default ? Style.current.darkGrey : Style.current.grey; enabled: false; icon: new Object({ - path: "../../../img/loading.png", + path: "../../app/img/loading.png", rotation: 0, runAnimation: true }) diff --git a/ui/shared/status/StatusStickerPackPurchaseModal.qml b/ui/shared/status/StatusStickerPackPurchaseModal.qml index c235e94938..e878a80a0d 100644 --- a/ui/shared/status/StatusStickerPackPurchaseModal.qml +++ b/ui/shared/status/StatusStickerPackPurchaseModal.qml @@ -99,9 +99,17 @@ ModalPopup { selectedGasLimit = 325000 return } - selectedGasLimit = chatsModel.stickers.estimate(root.stickerPackId, selectFromAccount.selectedAccount.address, root.packPrice) + chatsModel.stickers.estimate(root.stickerPackId, selectFromAccount.selectedAccount.address, root.packPrice, uuid) }) } + Connections { + target: chatsModel.stickers + onGasEstimateReturned: { + if (uuid === gasSelector.uuid) { + gasSelector.selectedGasLimit = estimate + } + } + } GasValidator { id: gasValidator anchors.bottom: parent.bottom diff --git a/ui/shared/status/StatusStickersPopup.qml b/ui/shared/status/StatusStickersPopup.qml index 6fd7c0328b..8327a956d4 100644 --- a/ui/shared/status/StatusStickersPopup.qml +++ b/ui/shared/status/StatusStickersPopup.qml @@ -37,6 +37,12 @@ Popup { footerContent.visible = true stickersContainer.visible = true } + Connections { + target: chatsModel + onOnlineStatusChanged: { + root.close() + } + } contentItem: ColumnLayout { anchors.fill: parent spacing: 0 diff --git a/vendor/DOtherSide b/vendor/DOtherSide index 5012620a2f..1cc16aaa5c 160000 --- a/vendor/DOtherSide +++ b/vendor/DOtherSide @@ -1 +1 @@ -Subproject commit 5012620a2ffbd9ac5c2b7c53671886d54c91a408 +Subproject commit 1cc16aaa5c643d0d33c31f9953fbe4a9f6bde151 diff --git a/vendor/nimqml b/vendor/nimqml index 953e9f4b38..29fca3ce2e 160000 --- a/vendor/nimqml +++ b/vendor/nimqml @@ -1 +1 @@ -Subproject commit 953e9f4b38478c835b9152103192220c49fc4128 +Subproject commit 29fca3ce2ed2c9bc0c99163bc2c199b6ed101dd6