fix(@deaktop/wallet): Implement error states with automatic retries
fixes #9688
This commit is contained in:
parent
054ad18532
commit
df121445ca
|
@ -32,6 +32,7 @@ import ../../app_service/service/mailservers/service as mailservers_service
|
||||||
import ../../app_service/service/gif/service as gif_service
|
import ../../app_service/service/gif/service as gif_service
|
||||||
import ../../app_service/service/ens/service as ens_service
|
import ../../app_service/service/ens/service as ens_service
|
||||||
import ../../app_service/service/community_tokens/service as tokens_service
|
import ../../app_service/service/community_tokens/service as tokens_service
|
||||||
|
import ../../app_service/service/network_connection/service as network_connection_service
|
||||||
|
|
||||||
import ../modules/shared_modules/keycard_popup/module as keycard_shared_module
|
import ../modules/shared_modules/keycard_popup/module as keycard_shared_module
|
||||||
import ../modules/startup/module as startup_module
|
import ../modules/startup/module as startup_module
|
||||||
|
@ -96,6 +97,7 @@ type
|
||||||
gifService: gif_service.Service
|
gifService: gif_service.Service
|
||||||
ensService: ens_service.Service
|
ensService: ens_service.Service
|
||||||
tokensService: tokens_service.Service
|
tokensService: tokens_service.Service
|
||||||
|
networkConnectionService: network_connection_service.Service
|
||||||
|
|
||||||
# Modules
|
# Modules
|
||||||
startupModule: startup_module.AccessInterface
|
startupModule: startup_module.AccessInterface
|
||||||
|
@ -216,6 +218,7 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
|
||||||
result.tokensService = tokens_service.newService(statusFoundation.events, statusFoundation.threadpool,
|
result.tokensService = tokens_service.newService(statusFoundation.events, statusFoundation.threadpool,
|
||||||
result.transactionService)
|
result.transactionService)
|
||||||
result.providerService = provider_service.newService(statusFoundation.events, statusFoundation.threadpool, result.ensService)
|
result.providerService = provider_service.newService(statusFoundation.events, statusFoundation.threadpool, result.ensService)
|
||||||
|
result.networkConnectionService = network_connection_service.newService(statusFoundation.events, result.walletAccountService, result.networkService, result.collectibleService, result.nodeService)
|
||||||
|
|
||||||
# Modules
|
# Modules
|
||||||
result.startupModule = startup_module.newModule[AppController](
|
result.startupModule = startup_module.newModule[AppController](
|
||||||
|
@ -264,7 +267,8 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
|
||||||
result.tokensService,
|
result.tokensService,
|
||||||
result.networkService,
|
result.networkService,
|
||||||
result.generalService,
|
result.generalService,
|
||||||
result.keycardService
|
result.keycardService,
|
||||||
|
result.networkConnectionService
|
||||||
)
|
)
|
||||||
|
|
||||||
# Do connections
|
# Do connections
|
||||||
|
@ -321,6 +325,7 @@ proc delete*(self: AppController) =
|
||||||
self.tokensService.delete
|
self.tokensService.delete
|
||||||
self.gifService.delete
|
self.gifService.delete
|
||||||
self.keycardService.delete
|
self.keycardService.delete
|
||||||
|
self.networkConnectionService.delete
|
||||||
|
|
||||||
proc disconnectKeychain(self: AppController) =
|
proc disconnectKeychain(self: AppController) =
|
||||||
for id in self.keychainConnectionIds:
|
for id in self.keychainConnectionIds:
|
||||||
|
@ -410,6 +415,7 @@ proc load(self: AppController) =
|
||||||
self.ensService.init()
|
self.ensService.init()
|
||||||
self.tokensService.init()
|
self.tokensService.init()
|
||||||
self.gifService.init()
|
self.gifService.init()
|
||||||
|
self.networkConnectionService.init()
|
||||||
|
|
||||||
# Accessible after user login
|
# Accessible after user login
|
||||||
singletonInstance.engine.setRootContextProperty("appSettings", self.appSettingsVariant)
|
singletonInstance.engine.setRootContextProperty("appSettings", self.appSettingsVariant)
|
||||||
|
|
|
@ -63,6 +63,9 @@ method browserSectionDidLoad*(self: AccessInterface) {.base.} =
|
||||||
method networksModuleDidLoad*(self: AccessInterface) {.base.} =
|
method networksModuleDidLoad*(self: AccessInterface) {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method networkConnectionModuleDidLoad*(self: AccessInterface) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
method nodeSectionDidLoad*(self: AccessInterface) {.base.} =
|
method nodeSectionDidLoad*(self: AccessInterface) {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ import communities/module as communities_module
|
||||||
import node_section/module as node_section_module
|
import node_section/module as node_section_module
|
||||||
import networks/module as networks_module
|
import networks/module as networks_module
|
||||||
import communities/tokens/models/token_item
|
import communities/tokens/models/token_item
|
||||||
|
import network_connection/module as network_connection_module
|
||||||
import ../../../app_service/service/contacts/dto/contacts
|
import ../../../app_service/service/contacts/dto/contacts
|
||||||
|
|
||||||
import ../../../app_service/service/keychain/service as keychain_service
|
import ../../../app_service/service/keychain/service as keychain_service
|
||||||
|
@ -57,6 +58,7 @@ import ../../../app_service/service/community_tokens/service as community_tokens
|
||||||
import ../../../app_service/service/network/service as network_service
|
import ../../../app_service/service/network/service as network_service
|
||||||
import ../../../app_service/service/general/service as general_service
|
import ../../../app_service/service/general/service as general_service
|
||||||
import ../../../app_service/service/keycard/service as keycard_service
|
import ../../../app_service/service/keycard/service as keycard_service
|
||||||
|
import ../../../app_service/service/network_connection/service as network_connection_service
|
||||||
import ../../../app_service/common/types
|
import ../../../app_service/common/types
|
||||||
import ../../../app_service/common/social_links
|
import ../../../app_service/common/social_links
|
||||||
|
|
||||||
|
@ -87,6 +89,7 @@ type
|
||||||
accountsService: accounts_service.Service
|
accountsService: accounts_service.Service
|
||||||
walletAccountService: wallet_account_service.Service
|
walletAccountService: wallet_account_service.Service
|
||||||
keychainService: keychain_service.Service
|
keychainService: keychain_service.Service
|
||||||
|
networkConnectionService: network_connection_service.Service
|
||||||
walletSectionModule: wallet_section_module.AccessInterface
|
walletSectionModule: wallet_section_module.AccessInterface
|
||||||
browserSectionModule: browser_section_module.AccessInterface
|
browserSectionModule: browser_section_module.AccessInterface
|
||||||
profileSectionModule: profile_section_module.AccessInterface
|
profileSectionModule: profile_section_module.AccessInterface
|
||||||
|
@ -98,6 +101,7 @@ type
|
||||||
networksModule: networks_module.AccessInterface
|
networksModule: networks_module.AccessInterface
|
||||||
keycardSharedModule: keycard_shared_module.AccessInterface
|
keycardSharedModule: keycard_shared_module.AccessInterface
|
||||||
keycardSharedModuleKeycardSyncPurpose: keycard_shared_module.AccessInterface
|
keycardSharedModuleKeycardSyncPurpose: keycard_shared_module.AccessInterface
|
||||||
|
networkConnectionModule: network_connection_module.AccessInterface
|
||||||
moduleLoaded: bool
|
moduleLoaded: bool
|
||||||
chatsLoaded: bool
|
chatsLoaded: bool
|
||||||
communityDataLoaded: bool
|
communityDataLoaded: bool
|
||||||
|
@ -141,7 +145,8 @@ proc newModule*[T](
|
||||||
communityTokensService: community_tokens_service.Service,
|
communityTokensService: community_tokens_service.Service,
|
||||||
networkService: network_service.Service,
|
networkService: network_service.Service,
|
||||||
generalService: general_service.Service,
|
generalService: general_service.Service,
|
||||||
keycardService: keycard_service.Service
|
keycardService: keycard_service.Service,
|
||||||
|
networkConnectionService: network_connection_service.Service
|
||||||
): Module[T] =
|
): Module[T] =
|
||||||
result = Module[T]()
|
result = Module[T]()
|
||||||
result.delegate = delegate
|
result.delegate = delegate
|
||||||
|
@ -206,6 +211,7 @@ proc newModule*[T](
|
||||||
messageService)
|
messageService)
|
||||||
result.nodeSectionModule = node_section_module.newModule(result, events, settingsService, nodeService, nodeConfigurationService)
|
result.nodeSectionModule = node_section_module.newModule(result, events, settingsService, nodeService, nodeConfigurationService)
|
||||||
result.networksModule = networks_module.newModule(result, events, networkService, walletAccountService, settingsService)
|
result.networksModule = networks_module.newModule(result, events, networkService, walletAccountService, settingsService)
|
||||||
|
result.networkConnectionModule = network_connection_module.newModule(result, events, networkConnectionService)
|
||||||
|
|
||||||
method delete*[T](self: Module[T]) =
|
method delete*[T](self: Module[T]) =
|
||||||
self.controller.delete
|
self.controller.delete
|
||||||
|
@ -225,6 +231,7 @@ method delete*[T](self: Module[T]) =
|
||||||
self.keycardSharedModule.delete
|
self.keycardSharedModule.delete
|
||||||
if not self.keycardSharedModuleKeycardSyncPurpose.isNil:
|
if not self.keycardSharedModuleKeycardSyncPurpose.isNil:
|
||||||
self.keycardSharedModuleKeycardSyncPurpose.delete
|
self.keycardSharedModuleKeycardSyncPurpose.delete
|
||||||
|
self.networkConnectionModule.delete
|
||||||
self.view.delete
|
self.view.delete
|
||||||
self.viewVariant.delete
|
self.viewVariant.delete
|
||||||
|
|
||||||
|
@ -498,6 +505,7 @@ method load*[T](
|
||||||
self.nodeSectionModule.load()
|
self.nodeSectionModule.load()
|
||||||
# Load wallet last as it triggers events that are listened by other modules
|
# Load wallet last as it triggers events that are listened by other modules
|
||||||
self.walletSectionModule.load()
|
self.walletSectionModule.load()
|
||||||
|
self.networkConnectionModule.load()
|
||||||
|
|
||||||
# Set active section on app start
|
# Set active section on app start
|
||||||
# If section is empty or profile then open the loading section until chats are loaded
|
# If section is empty or profile then open the loading section until chats are loaded
|
||||||
|
@ -657,6 +665,9 @@ proc checkIfModuleDidLoad [T](self: Module[T]) =
|
||||||
if(not self.networksModule.isLoaded()):
|
if(not self.networksModule.isLoaded()):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if(not self.networkConnectionModule.isLoaded()):
|
||||||
|
return
|
||||||
|
|
||||||
self.moduleLoaded = true
|
self.moduleLoaded = true
|
||||||
self.delegate.mainDidLoad()
|
self.delegate.mainDidLoad()
|
||||||
|
|
||||||
|
@ -696,6 +707,9 @@ method nodeSectionDidLoad*[T](self: Module[T]) =
|
||||||
method networksModuleDidLoad*[T](self: Module[T]) =
|
method networksModuleDidLoad*[T](self: Module[T]) =
|
||||||
self.checkIfModuleDidLoad()
|
self.checkIfModuleDidLoad()
|
||||||
|
|
||||||
|
method networkConnectionModuleDidLoad*[T](self: Module[T]) =
|
||||||
|
self.checkIfModuleDidLoad()
|
||||||
|
|
||||||
method viewDidLoad*[T](self: Module[T]) =
|
method viewDidLoad*[T](self: Module[T]) =
|
||||||
self.checkIfModuleDidLoad()
|
self.checkIfModuleDidLoad()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
import NimQml
|
||||||
|
|
||||||
|
import ./io_interface
|
||||||
|
import ../../../core/eventemitter
|
||||||
|
import ../../../../app_service/service/network_connection/service as network_connection_service
|
||||||
|
import ../../../../app_service/service/node/service as node_service
|
||||||
|
|
||||||
|
type
|
||||||
|
Controller* = ref object of RootObj
|
||||||
|
delegate: io_interface.AccessInterface
|
||||||
|
events: EventEmitter
|
||||||
|
networkConnectionService: network_connection_service.Service
|
||||||
|
|
||||||
|
proc newController*(
|
||||||
|
delegate: io_interface.AccessInterface,
|
||||||
|
events: EventEmitter,
|
||||||
|
networkConnectionService: network_connection_service.Service
|
||||||
|
): Controller =
|
||||||
|
result = Controller()
|
||||||
|
result.delegate = delegate
|
||||||
|
result.events = events
|
||||||
|
result.networkConnectionService = networkConnectionService
|
||||||
|
|
||||||
|
proc delete*(self: Controller) =
|
||||||
|
discard
|
||||||
|
|
||||||
|
proc init*(self: Controller) =
|
||||||
|
self.events.on(SIGNAL_CONNECTION_UPDATE) do(e:Args):
|
||||||
|
let args = NetworkConnectionsArgs(e)
|
||||||
|
self.delegate.networkConnectionStatusUpdate(args.website, args.completelyDown, ord(args.connectionState), args.chainIds, args.lastCheckedAt, args.timeToAutoRetryInSecs, args.withCache)
|
||||||
|
|
||||||
|
self.events.on(SIGNAL_NETWORK_CONNECTED) do(e: Args):
|
||||||
|
self.networkConnectionService.networkConnected()
|
||||||
|
|
||||||
|
proc refreshBlockchainValues*(self: Controller) =
|
||||||
|
self.networkConnectionService.blockchainsRetry()
|
||||||
|
|
||||||
|
proc refreshMarketValues*(self: Controller) =
|
||||||
|
self.networkConnectionService.marketRetry()
|
||||||
|
|
||||||
|
proc refreshCollectiblesValues*(self: Controller) =
|
||||||
|
self.networkConnectionService.collectiblesRetry()
|
|
@ -0,0 +1,32 @@
|
||||||
|
import NimQml
|
||||||
|
|
||||||
|
type
|
||||||
|
AccessInterface* {.pure inheritable.} = ref object of RootObj
|
||||||
|
## Abstract class for any input/interaction with this module.
|
||||||
|
|
||||||
|
method delete*(self: AccessInterface) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method load*(self: AccessInterface) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method isLoaded*(self: AccessInterface): bool {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
# View Delegate Interface
|
||||||
|
# Delegate for the view must be declared here due to use of QtObject and multi
|
||||||
|
# inheritance, which is not well supported in Nim.
|
||||||
|
method viewDidLoad*(self: AccessInterface) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method networkConnectionStatusUpdate*(self: AccessInterface, website: string, completelyDown: bool, connectionState: int, chainIds: string, lastCheckedAt: int, timeToAutoRetryInSecs: int, withCache: bool) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method refreshBlockchainValues*(self: AccessInterface) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method refreshMarketValues*(self: AccessInterface) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method refreshCollectiblesValues*(self: AccessInterface) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
|
@ -0,0 +1,62 @@
|
||||||
|
import NimQml
|
||||||
|
|
||||||
|
import io_interface, view, controller
|
||||||
|
import ../io_interface as delegate_interface
|
||||||
|
import ../../../global/global_singleton
|
||||||
|
import ../../../core/eventemitter
|
||||||
|
import ../../../../app_service/service/network_connection/service as network_connection_service
|
||||||
|
|
||||||
|
export io_interface
|
||||||
|
|
||||||
|
type
|
||||||
|
Module* = ref object of io_interface.AccessInterface
|
||||||
|
delegate: delegate_interface.AccessInterface
|
||||||
|
view: View
|
||||||
|
viewVariant: QVariant
|
||||||
|
controller: Controller
|
||||||
|
moduleLoaded: bool
|
||||||
|
|
||||||
|
proc newModule*(
|
||||||
|
delegate: delegate_interface.AccessInterface,
|
||||||
|
events: EventEmitter,
|
||||||
|
networkConnectionService: network_connection_service.Service,
|
||||||
|
): Module =
|
||||||
|
result = Module()
|
||||||
|
result.delegate = delegate
|
||||||
|
result.view = view.newView(result)
|
||||||
|
result.viewVariant = newQVariant(result.view)
|
||||||
|
result.controller = controller.newController(result, events, networkConnectionService)
|
||||||
|
result.moduleLoaded = false
|
||||||
|
|
||||||
|
singletonInstance.engine.setRootContextProperty("networkConnectionModule", result.viewVariant)
|
||||||
|
|
||||||
|
method delete*(self: Module) =
|
||||||
|
self.view.delete
|
||||||
|
self.viewVariant.delete
|
||||||
|
self.controller.delete
|
||||||
|
|
||||||
|
method load*(self: Module) =
|
||||||
|
self.controller.init()
|
||||||
|
self.view.load()
|
||||||
|
|
||||||
|
method isLoaded*(self: Module): bool =
|
||||||
|
return self.moduleLoaded
|
||||||
|
|
||||||
|
proc checkIfModuleDidLoad(self: Module) =
|
||||||
|
self.moduleLoaded = true
|
||||||
|
self.delegate.networkConnectionModuleDidLoad()
|
||||||
|
|
||||||
|
method viewDidLoad*(self: Module) =
|
||||||
|
self.checkIfModuleDidLoad()
|
||||||
|
|
||||||
|
method networkConnectionStatusUpdate*(self: Module, website: string, completelyDown: bool, connectionState: int, chainIds: string, lastCheckedAt: int, timeToAutoRetryInSecs: int, withCache: bool) =
|
||||||
|
self.view.networkConnectionStatusUpdate(website, completelyDown, connectionState, chainIds, lastCheckedAt, timeToAutoRetryInSecs, withCache)
|
||||||
|
|
||||||
|
method refreshBlockchainValues*(self: Module) =
|
||||||
|
self.controller.refreshBlockchainValues()
|
||||||
|
|
||||||
|
method refreshMarketValues*(self: Module) =
|
||||||
|
self.controller.refreshMarketValues()
|
||||||
|
|
||||||
|
method refreshCollectiblesValues*(self: Module) =
|
||||||
|
self.controller.refreshCollectiblesValues()
|
|
@ -0,0 +1,34 @@
|
||||||
|
import NimQml
|
||||||
|
|
||||||
|
import ./io_interface
|
||||||
|
|
||||||
|
QtObject:
|
||||||
|
type
|
||||||
|
View* = ref object of QObject
|
||||||
|
delegate: io_interface.AccessInterface
|
||||||
|
|
||||||
|
proc setup(self: View) =
|
||||||
|
self.QObject.setup
|
||||||
|
|
||||||
|
proc delete*(self: View) =
|
||||||
|
self.QObject.delete
|
||||||
|
|
||||||
|
proc newView*(delegate: io_interface.AccessInterface): View =
|
||||||
|
new(result, delete)
|
||||||
|
result.delegate = delegate
|
||||||
|
result.setup()
|
||||||
|
|
||||||
|
proc load*(self: View) =
|
||||||
|
self.delegate.viewDidLoad()
|
||||||
|
|
||||||
|
proc networkConnectionStatusUpdate*(self: View, website: string, completelyDown: bool, connectionState: int, chainIds: string, lastCheckedAt: int, timeToAutoRetryInSecs: int, withCache: bool) {.signal.}
|
||||||
|
|
||||||
|
proc refreshBlockchainValues*(self: View) {.slot.} =
|
||||||
|
self.delegate.refreshBlockchainValues()
|
||||||
|
|
||||||
|
proc refreshMarketValues*(self: View) {.slot.} =
|
||||||
|
self.delegate.refreshMarketValues()
|
||||||
|
|
||||||
|
proc refreshCollectiblesValues*(self: View) {.slot.} =
|
||||||
|
self.delegate.refreshCollectiblesValues()
|
||||||
|
|
|
@ -3,6 +3,7 @@ import io_interface
|
||||||
import ../../../../../app_service/service/collectible/service as collectible_service
|
import ../../../../../app_service/service/collectible/service as collectible_service
|
||||||
import ../../../../../app_service/service/wallet_account/service as wallet_account_service
|
import ../../../../../app_service/service/wallet_account/service as wallet_account_service
|
||||||
import ../../../../../app_service/service/network/service as network_service
|
import ../../../../../app_service/service/network/service as network_service
|
||||||
|
import ../../../../../app_service/service/network_connection/service as network_connection_service
|
||||||
import ../../../../core/eventemitter
|
import ../../../../core/eventemitter
|
||||||
|
|
||||||
type
|
type
|
||||||
|
@ -50,6 +51,12 @@ proc init*(self: Controller) =
|
||||||
let args = OwnedCollectiblesUpdateArgs(e)
|
let args = OwnedCollectiblesUpdateArgs(e)
|
||||||
self.refreshCollectibles(args.chainId, args.address)
|
self.refreshCollectibles(args.chainId, args.address)
|
||||||
|
|
||||||
|
self.events.on(SIGNAL_REFRESH_COLLECTIBLES) do(e:Args):
|
||||||
|
let args = RetryCollectibleArgs(e)
|
||||||
|
let chainId = self.networkService.getNetworkForCollectibles().chainId
|
||||||
|
for address in args.addresses:
|
||||||
|
self.refreshCollectibles(chainId, address)
|
||||||
|
|
||||||
proc getWalletAccount*(self: Controller, accountIndex: int): wallet_account_service.WalletAccountDto =
|
proc getWalletAccount*(self: Controller, accountIndex: int): wallet_account_service.WalletAccountDto =
|
||||||
return self.walletAccountService.getWalletAccount(accountIndex)
|
return self.walletAccountService.getWalletAccount(accountIndex)
|
||||||
|
|
||||||
|
|
|
@ -155,6 +155,14 @@ QtObject:
|
||||||
of "wallet-tick-reload":
|
of "wallet-tick-reload":
|
||||||
self.resetAllOwnedCollectibles()
|
self.resetAllOwnedCollectibles()
|
||||||
|
|
||||||
|
# needs to be re-written once cache for colletibles works
|
||||||
|
proc areCollectionsLoaded*(self: Service): bool =
|
||||||
|
for chainId, adressesData in self.ownershipData:
|
||||||
|
for address, collectionsData in adressesData:
|
||||||
|
if collectionsData.allLoaded:
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
|
||||||
proc prepareOwnershipData(self: Service, chainId: int, address: string) =
|
proc prepareOwnershipData(self: Service, chainId: int, address: string) =
|
||||||
if not self.accountsOwnershipData.hasKey(chainId):
|
if not self.accountsOwnershipData.hasKey(chainId):
|
||||||
self.accountsOwnershipData[chainId] = newAddressesData()
|
self.accountsOwnershipData[chainId] = newAddressesData()
|
||||||
|
@ -428,4 +436,4 @@ QtObject:
|
||||||
proc resetAllOwnedCollectibles*(self: Service) =
|
proc resetAllOwnedCollectibles*(self: Service) =
|
||||||
for chainId, addressesData in self.accountsOwnershipData:
|
for chainId, addressesData in self.accountsOwnershipData:
|
||||||
for address, _ in addressesData:
|
for address, _ in addressesData:
|
||||||
self.resetOwnedCollectibles(chainId, address)
|
self.resetOwnedCollectibles(chainId, address)
|
||||||
|
|
|
@ -0,0 +1,265 @@
|
||||||
|
import NimQml, chronicles, Tables, strutils, sequtils, sugar, strformat, json
|
||||||
|
|
||||||
|
import ../../../app/global/global_singleton
|
||||||
|
import ../../../app/core/eventemitter
|
||||||
|
import ../../../app/core/signals/types
|
||||||
|
|
||||||
|
import ../collectible/service as collectible_service
|
||||||
|
import ../wallet_account/service as wallet_service
|
||||||
|
import ../network/service as network_service
|
||||||
|
import ../node/service as node_service
|
||||||
|
import ../../../backend/backend as backend
|
||||||
|
|
||||||
|
logScope:
|
||||||
|
topics = "network-connection-service"
|
||||||
|
|
||||||
|
type ConnectionState* {.pure.} = enum
|
||||||
|
Successful = 0,
|
||||||
|
Failed = 1,
|
||||||
|
Retrying = 2
|
||||||
|
|
||||||
|
type ConnectionStatus* = ref object of RootObj
|
||||||
|
connectionState*: ConnectionState
|
||||||
|
completelyDown*: bool
|
||||||
|
chainIds*: seq[int]
|
||||||
|
lastCheckedAt*: int
|
||||||
|
timeToAutoRetryInSecs*: int
|
||||||
|
timer*: QTimer
|
||||||
|
withCache*: bool
|
||||||
|
|
||||||
|
const SIGNAL_CONNECTION_UPDATE* = "signalConnectionUpdate"
|
||||||
|
const SIGNAL_REFRESH_COLLECTIBLES* = "signalRefreshCollectibles"
|
||||||
|
|
||||||
|
type NetworkConnectionsArgs* = ref object of Args
|
||||||
|
website*: string
|
||||||
|
completelyDown*: bool
|
||||||
|
connectionState*: ConnectionState
|
||||||
|
chainIds*: string
|
||||||
|
lastCheckedAt*: int
|
||||||
|
timeToAutoRetryInSecs*: int
|
||||||
|
withCache*: bool
|
||||||
|
|
||||||
|
type RetryCollectibleArgs* = ref object of Args
|
||||||
|
addresses*: seq[string]
|
||||||
|
|
||||||
|
const BLOCKCHAINS* = "blockchains"
|
||||||
|
const MARKET* = "market"
|
||||||
|
const COLLECTIBLES* = "collectibles"
|
||||||
|
const BACKOFF_TIMERS* = [30, 60, 180, 600, 3600, 10800]
|
||||||
|
|
||||||
|
include ../../common/json_utils
|
||||||
|
|
||||||
|
proc newConnectionStatus(): ConnectionStatus =
|
||||||
|
return ConnectionStatus(
|
||||||
|
connectionState: ConnectionState.Successful,
|
||||||
|
completelyDown: false,
|
||||||
|
chainIds: @[],
|
||||||
|
lastCheckedAt: 0,
|
||||||
|
timeToAutoRetryInSecs: BACKOFF_TIMERS[0],
|
||||||
|
timer: newQTimer(),
|
||||||
|
withCache: false
|
||||||
|
)
|
||||||
|
|
||||||
|
QtObject:
|
||||||
|
type Service* = ref object of QObject
|
||||||
|
closingApp: bool
|
||||||
|
events: EventEmitter
|
||||||
|
walletService: wallet_service.Service
|
||||||
|
networkService: network_service.Service
|
||||||
|
collectibleService: collectible_service.Service
|
||||||
|
nodeService: node_service.Service
|
||||||
|
connectionStatus: Table[string, ConnectionStatus]
|
||||||
|
|
||||||
|
# Forward declaration
|
||||||
|
proc checkConnected(self: Service)
|
||||||
|
|
||||||
|
proc delete*(self: Service) =
|
||||||
|
self.closingApp = true
|
||||||
|
self.QObject.delete
|
||||||
|
|
||||||
|
proc newService*(
|
||||||
|
events: EventEmitter,
|
||||||
|
walletService: wallet_service.Service,
|
||||||
|
networkService: network_service.Service,
|
||||||
|
collectibleService: collectible_service.Service,
|
||||||
|
nodeService: node_service.Service,
|
||||||
|
): Service =
|
||||||
|
new(result, delete)
|
||||||
|
result.QObject.setup
|
||||||
|
result.closingApp = false
|
||||||
|
result.events = events
|
||||||
|
result.walletService = walletService
|
||||||
|
result.networkService = networkService
|
||||||
|
result.collectibleService = collectibleService
|
||||||
|
result.nodeService = nodeService
|
||||||
|
result.connectionStatus = {BLOCKCHAINS: newConnectionStatus(),
|
||||||
|
MARKET: newConnectionStatus(),
|
||||||
|
COLLECTIBLES: newConnectionStatus()}.toTable
|
||||||
|
|
||||||
|
proc init*(self: Service) =
|
||||||
|
self.events.on(SignalType.Wallet.event) do(e:Args):
|
||||||
|
var data = WalletSignal(e)
|
||||||
|
case data.eventType:
|
||||||
|
of "wallet-tick-check-connected":
|
||||||
|
self.checkConnected()
|
||||||
|
|
||||||
|
proc getFormattedStringForChainIds(self: Service, chainIds: seq[int]): string =
|
||||||
|
var result: string = ""
|
||||||
|
for chainId in chainIds:
|
||||||
|
if result.isEmptyOrWhitespace:
|
||||||
|
result = $chainId
|
||||||
|
else:
|
||||||
|
result = result & ";" & $chainId
|
||||||
|
return result
|
||||||
|
|
||||||
|
proc convertConnectionStatusToNetworkConnectionsArgs(self: Service, website: string, connectionStatus: ConnectionStatus): NetworkConnectionsArgs =
|
||||||
|
result = NetworkConnectionsArgs(
|
||||||
|
website: website,
|
||||||
|
completelyDown: connectionStatus.completelyDown,
|
||||||
|
connectionState: connectionStatus.connectionState,
|
||||||
|
chainIds: self.getFormattedStringForChainIds(connectionStatus.chainIds),
|
||||||
|
lastCheckedAt: connectionStatus.lastCheckedAt,
|
||||||
|
timeToAutoRetryInSecs: connectionStatus.timeToAutoRetryInSecs,
|
||||||
|
withCache: connectionStatus.withCache
|
||||||
|
)
|
||||||
|
|
||||||
|
proc blockchainsRetry*(self: Service) {.slot.} =
|
||||||
|
if(self.connectionStatus.hasKey(BLOCKCHAINS)):
|
||||||
|
self.connectionStatus[BLOCKCHAINS].timer.stop()
|
||||||
|
self.connectionStatus[BLOCKCHAINS].connectionState = ConnectionState.Retrying
|
||||||
|
self.events.emit(SIGNAL_CONNECTION_UPDATE, self.convertConnectionStatusToNetworkConnectionsArgs(BLOCKCHAINS, self.connectionStatus[BLOCKCHAINS]))
|
||||||
|
self.walletService.reloadAccountTokens()
|
||||||
|
|
||||||
|
proc marketRetry*(self: Service) {.slot.} =
|
||||||
|
if(self.connectionStatus.hasKey(MARKET)):
|
||||||
|
self.connectionStatus[MARKET].timer.stop()
|
||||||
|
self.connectionStatus[MARKET].connectionState = ConnectionState.Retrying
|
||||||
|
self.events.emit(SIGNAL_CONNECTION_UPDATE, self.convertConnectionStatusToNetworkConnectionsArgs(MARKET, self.connectionStatus[MARKET]))
|
||||||
|
self.walletService.reloadAccountTokens()
|
||||||
|
|
||||||
|
proc collectiblesRetry*(self: Service) {.slot.} =
|
||||||
|
if(self.connectionStatus.hasKey(COLLECTIBLES)):
|
||||||
|
self.connectionStatus[COLLECTIBLES].timer.stop()
|
||||||
|
self.connectionStatus[COLLECTIBLES].connectionState = ConnectionState.Retrying
|
||||||
|
self.events.emit(SIGNAL_CONNECTION_UPDATE, self.convertConnectionStatusToNetworkConnectionsArgs(COLLECTIBLES, self.connectionStatus[COLLECTIBLES]))
|
||||||
|
self.events.emit(SIGNAL_REFRESH_COLLECTIBLES, RetryCollectibleArgs(addresses: self.walletService.getAddresses()))
|
||||||
|
|
||||||
|
# needs to be re-written once cache for market, blockchain and collectibles is implemented
|
||||||
|
proc hasCache(self: Service, website: string): bool =
|
||||||
|
case website:
|
||||||
|
of BLOCKCHAINS:
|
||||||
|
return self.walletService.hasCache()
|
||||||
|
of MARKET:
|
||||||
|
return self.walletService.hasCache()
|
||||||
|
of COLLECTIBLES:
|
||||||
|
return self.collectibleService.areCollectionsLoaded()
|
||||||
|
|
||||||
|
proc checkStatus(self: Service, status: JsonNode, website: string) =
|
||||||
|
var allDown: bool = true
|
||||||
|
var lastCheckedAt: int = 0
|
||||||
|
var chaindIdsDown: seq[int] = @[]
|
||||||
|
|
||||||
|
# checking only for networks currently active (for test net only testnet networks etc...)
|
||||||
|
let currentChainIds = self.networkService.getNetworks().map(a => a.chainId)
|
||||||
|
for chainId, state in status:
|
||||||
|
if state["up"].getBool:
|
||||||
|
allDown = false
|
||||||
|
# only add chains that belong to the test node or not based on current user setting
|
||||||
|
if currentChainIds.contains(chainId.parseInt):
|
||||||
|
lastCheckedAt = state["lastCheckedAt"].getInt
|
||||||
|
if not state["up"].getBool:
|
||||||
|
chaindIdsDown.add(chainId.parseInt)
|
||||||
|
|
||||||
|
if self.connectionStatus.hasKey(website):
|
||||||
|
self.connectionStatus[website].withCache = self.hasCache(website)
|
||||||
|
# if all the networks are down for the website
|
||||||
|
if allDown:
|
||||||
|
if not self.connectionStatus[website].timer.isActive():
|
||||||
|
var backOffTimer: int = self.connectionStatus[website].timeToAutoRetryInSecs
|
||||||
|
|
||||||
|
# if all the networks are down for the website after a retry increment the backoff timer
|
||||||
|
if self.connectionStatus[website].completelyDown and self.connectionStatus[website].connectionState == ConnectionState.Retrying:
|
||||||
|
let index = BACKOFF_TIMERS.find(self.connectionStatus[website].timeToAutoRetryInSecs)
|
||||||
|
if index != -1 and index < BACKOFF_TIMERS.len:
|
||||||
|
backOffTimer = BACKOFF_TIMERS[index + 1]
|
||||||
|
|
||||||
|
self.connectionStatus[website].connectionState = ConnectionState.Failed
|
||||||
|
self.connectionStatus[website].completelyDown = true
|
||||||
|
self.connectionStatus[website].lastCheckedAt = lastCheckedAt
|
||||||
|
self.connectionStatus[website].timeToAutoRetryInSecs = backOffTimer
|
||||||
|
self.connectionStatus[website].chainIds = chaindIdsDown
|
||||||
|
signalConnect(self.connectionStatus[website].timer, "timeout()", self, website&"Retry()", 2)
|
||||||
|
self.connectionStatus[website].timer.setInterval(backOffTimer*1000)
|
||||||
|
self.connectionStatus[website].timer.start()
|
||||||
|
|
||||||
|
self.events.emit(SIGNAL_CONNECTION_UPDATE, self.convertConnectionStatusToNetworkConnectionsArgs(website, self.connectionStatus[website]))
|
||||||
|
|
||||||
|
# if all the networks are not down for the website
|
||||||
|
else:
|
||||||
|
# case where a down website is back up
|
||||||
|
if self.connectionStatus[website].completelyDown or (chaindIdsDown.len == 0 and self.connectionStatus[website].chainIds.len != 0):
|
||||||
|
self.connectionStatus[website] = newConnectionStatus()
|
||||||
|
self.events.emit(SIGNAL_CONNECTION_UPDATE, self.convertConnectionStatusToNetworkConnectionsArgs(website, self.connectionStatus[website]))
|
||||||
|
|
||||||
|
# case where a some of networks on the website are down
|
||||||
|
if chaindIdsDown.len > 0:
|
||||||
|
var backOffTimer: int = self.connectionStatus[website].timeToAutoRetryInSecs
|
||||||
|
if self.connectionStatus[website].connectionState == ConnectionState.Retrying:
|
||||||
|
let index = BACKOFF_TIMERS.find(self.connectionStatus[website].timeToAutoRetryInSecs)
|
||||||
|
if index != -1 and index < BACKOFF_TIMERS.len:
|
||||||
|
backOffTimer = BACKOFF_TIMERS[index + 1]
|
||||||
|
|
||||||
|
self.connectionStatus[website].completelyDown = false
|
||||||
|
self.connectionStatus[website].chainIds = chaindIdsDown
|
||||||
|
self.connectionStatus[website].timeToAutoRetryInSecs = backOffTimer
|
||||||
|
self.connectionStatus[website].connectionState = ConnectionState.Failed
|
||||||
|
self.connectionStatus[website].lastCheckedAt = lastCheckedAt
|
||||||
|
signalConnect(self.connectionStatus[website].timer, "timeout()", self, website&"Retry()", 2)
|
||||||
|
self.connectionStatus[website].timer.setInterval(self.connectionStatus[website].timeToAutoRetryInSecs*1000)
|
||||||
|
self.connectionStatus[website].timer.start()
|
||||||
|
|
||||||
|
self.events.emit(SIGNAL_CONNECTION_UPDATE, self.convertConnectionStatusToNetworkConnectionsArgs(website, self.connectionStatus[website]))
|
||||||
|
|
||||||
|
proc checkMarketStatus(self: Service, status: JsonNode, website: string) =
|
||||||
|
if self.connectionStatus.hasKey(website):
|
||||||
|
self.connectionStatus[website].withCache = self.hasCache(website)
|
||||||
|
if not status["up"].getBool:
|
||||||
|
if not self.connectionStatus[website].timer.isActive():
|
||||||
|
var backOffTimer: int = self.connectionStatus[website].timeToAutoRetryInSecs
|
||||||
|
if self.connectionStatus[website].connectionState == ConnectionState.Retrying:
|
||||||
|
let index = BACKOFF_TIMERS.find(self.connectionStatus[website].timeToAutoRetryInSecs)
|
||||||
|
if index != -1 and index < BACKOFF_TIMERS.len:
|
||||||
|
backOffTimer = BACKOFF_TIMERS[index + 1]
|
||||||
|
|
||||||
|
self.connectionStatus[website].completelyDown = true
|
||||||
|
self.connectionStatus[website].connectionState = ConnectionState.Failed
|
||||||
|
self.connectionStatus[website].timeToAutoRetryInSecs = backOffTimer
|
||||||
|
self.connectionStatus[website].lastCheckedAt = status["lastCheckedAt"].getInt
|
||||||
|
signalConnect(self.connectionStatus[website].timer, "timeout()", self, website&"Retry()", 2)
|
||||||
|
self.connectionStatus[website].timer.setInterval(self.connectionStatus[website].timeToAutoRetryInSecs*1000)
|
||||||
|
self.connectionStatus[website].timer.start()
|
||||||
|
self.events.emit(SIGNAL_CONNECTION_UPDATE,self.convertConnectionStatusToNetworkConnectionsArgs(website, self.connectionStatus[website]))
|
||||||
|
else:
|
||||||
|
# site was completely down and is back up now
|
||||||
|
if self.connectionStatus[website].completelyDown:
|
||||||
|
self.connectionStatus[website] = newConnectionStatus()
|
||||||
|
self.events.emit(SIGNAL_CONNECTION_UPDATE, self.convertConnectionStatusToNetworkConnectionsArgs(website, self.connectionStatus[website]))
|
||||||
|
|
||||||
|
proc checkConnected(self: Service) =
|
||||||
|
if(not singletonInstance.localAccountSensitiveSettings.getIsWalletEnabled()):
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self.nodeService.isConnected():
|
||||||
|
let response = backend.checkConnected()
|
||||||
|
self.checkStatus(response.result[BLOCKCHAINS], BLOCKCHAINS)
|
||||||
|
self.checkStatus(response.result[COLLECTIBLES], COLLECTIBLES)
|
||||||
|
self.checkMarketStatus(response.result[MARKET], MARKET)
|
||||||
|
except Exception as e:
|
||||||
|
let errDescription = e.msg
|
||||||
|
error "error: ", errDescription
|
||||||
|
return
|
||||||
|
|
||||||
|
proc networkConnected*(self: Service) =
|
||||||
|
self.walletService.reloadAccountTokens()
|
||||||
|
self.events.emit(SIGNAL_REFRESH_COLLECTIBLES, RetryCollectibleArgs(addresses: self.walletService.getAddresses()))
|
|
@ -125,7 +125,6 @@ QtObject:
|
||||||
# Forward declaration
|
# Forward declaration
|
||||||
proc buildAllTokens(self: Service, accounts: seq[string], store: bool)
|
proc buildAllTokens(self: Service, accounts: seq[string], store: bool)
|
||||||
proc checkRecentHistory*(self: Service)
|
proc checkRecentHistory*(self: Service)
|
||||||
proc checkConnected(self: Service)
|
|
||||||
proc startWallet(self: Service)
|
proc startWallet(self: Service)
|
||||||
proc handleKeycardActions(self: Service, keycardActions: seq[KeycardActionDto])
|
proc handleKeycardActions(self: Service, keycardActions: seq[KeycardActionDto])
|
||||||
proc handleKeycardsState(self: Service, keycardsState: seq[KeyPairDto])
|
proc handleKeycardsState(self: Service, keycardsState: seq[KeyPairDto])
|
||||||
|
@ -217,7 +216,7 @@ QtObject:
|
||||||
withLock self.walletAccountsLock:
|
withLock self.walletAccountsLock:
|
||||||
result = toSeq(self.walletAccounts.values)
|
result = toSeq(self.walletAccounts.values)
|
||||||
|
|
||||||
proc getAddresses(self: Service): seq[string] =
|
proc getAddresses*(self: Service): seq[string] =
|
||||||
withLock self.walletAccountsLock:
|
withLock self.walletAccountsLock:
|
||||||
result = toSeq(self.walletAccounts.keys())
|
result = toSeq(self.walletAccounts.keys())
|
||||||
|
|
||||||
|
@ -253,8 +252,10 @@ QtObject:
|
||||||
of "wallet-tick-reload":
|
of "wallet-tick-reload":
|
||||||
self.buildAllTokens(self.getAddresses(), store = true)
|
self.buildAllTokens(self.getAddresses(), store = true)
|
||||||
self.checkRecentHistory()
|
self.checkRecentHistory()
|
||||||
of "wallet-tick-check-connected":
|
|
||||||
self.checkConnected()
|
proc reloadAccountTokens*(self: Service) =
|
||||||
|
self.buildAllTokens(self.getAddresses(), store = true)
|
||||||
|
self.checkRecentHistory()
|
||||||
|
|
||||||
proc getWalletAccount*(self: Service, accountIndex: int): WalletAccountDto =
|
proc getWalletAccount*(self: Service, accountIndex: int): WalletAccountDto =
|
||||||
let accounts = self.getWalletAccounts()
|
let accounts = self.getWalletAccounts()
|
||||||
|
@ -274,19 +275,6 @@ QtObject:
|
||||||
|
|
||||||
discard backend.startWallet()
|
discard backend.startWallet()
|
||||||
|
|
||||||
proc checkConnected(self: Service) =
|
|
||||||
if(not main_constants.WALLET_ENABLED):
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
# TODO: add event for UI (Waiting for design)
|
|
||||||
discard backend.checkConnected()
|
|
||||||
except Exception as e:
|
|
||||||
let errDescription = e.msg
|
|
||||||
error "error: ", errDescription
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
proc checkRecentHistory*(self: Service) =
|
proc checkRecentHistory*(self: Service) =
|
||||||
if(not main_constants.WALLET_ENABLED):
|
if(not main_constants.WALLET_ENABLED):
|
||||||
return
|
return
|
||||||
|
@ -855,3 +843,11 @@ QtObject:
|
||||||
totalTokenBalance += token.getTotalBalanceOfSupportedChains()
|
totalTokenBalance += token.getTotalBalanceOfSupportedChains()
|
||||||
|
|
||||||
return totalTokenBalance
|
return totalTokenBalance
|
||||||
|
|
||||||
|
# needs to be re-written once cache for market, blockchain and collectibles is implemented
|
||||||
|
proc hasCache*(self: Service): bool =
|
||||||
|
withLock self.walletAccountsLock:
|
||||||
|
for address, accountDto in self.walletAccounts:
|
||||||
|
if self.walletAccounts[address].tokens.len > 0:
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
|
|
@ -156,4 +156,29 @@ QtObject {
|
||||||
function resolveENS(value) {
|
function resolveENS(value) {
|
||||||
mainModuleInst.resolveENS(value, "")
|
mainModuleInst.resolveENS(value, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property var networkConnectionModuleInst: networkConnectionModule
|
||||||
|
|
||||||
|
function getChainIdsJointString(chainIdsDown) {
|
||||||
|
let jointChainIdString = ""
|
||||||
|
for (const chain of chainIdsDown) {
|
||||||
|
jointChainIdString = (!!jointChainIdString) ? jointChainIdString + " & " : jointChainIdString
|
||||||
|
jointChainIdString += allNetworks.getNetworkFullName(parseInt(chain))
|
||||||
|
}
|
||||||
|
return jointChainIdString
|
||||||
|
}
|
||||||
|
|
||||||
|
function retryConnection(websiteDown) {
|
||||||
|
switch(websiteDown) {
|
||||||
|
case Constants.walletConnections.blockchains:
|
||||||
|
networkConnectionModule.refreshBlockchainValues()
|
||||||
|
break
|
||||||
|
case Constants.walletConnections.market:
|
||||||
|
networkConnectionModule.refreshMarketValues()
|
||||||
|
break
|
||||||
|
case Constants.walletConnections.collectibles:
|
||||||
|
networkConnectionModule.refreshCollectiblesValues()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -602,7 +602,7 @@ Item {
|
||||||
|
|
||||||
objectName: "connectionInfoBanner"
|
objectName: "connectionInfoBanner"
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: isConnected ? qsTr("Connected") : qsTr("Disconnected")
|
text: isConnected ? qsTr("You are back online") : qsTr("Internet connection lost. Reconnect to ensure everything is up to date.")
|
||||||
type: isConnected ? ModuleWarning.Success : ModuleWarning.Danger
|
type: isConnected ? ModuleWarning.Success : ModuleWarning.Danger
|
||||||
|
|
||||||
function updateState() {
|
function updateState() {
|
||||||
|
@ -676,6 +676,105 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConnectionWarnings {
|
||||||
|
id: walletBlockchainConnectionBanner
|
||||||
|
objectName: "walletBlockchainConnectionBanner"
|
||||||
|
readonly property string jointChainIdString: appMain.rootStore.getChainIdsJointString(chainIdsDown)
|
||||||
|
Layout.fillWidth: true
|
||||||
|
websiteDown: Constants.walletConnections.blockchains
|
||||||
|
text: {
|
||||||
|
switch(connectionState) {
|
||||||
|
case Constants.ConnectionStatus.Success:
|
||||||
|
return qsTr("Pocket Network (POKT) connection successful")
|
||||||
|
case Constants.ConnectionStatus.Failure:
|
||||||
|
if(completelyDown) {
|
||||||
|
updateTimer.restart()
|
||||||
|
if(withCache)
|
||||||
|
return qsTr("POKT & Infura down. Token balances are as of %1. Retrying in %2.").arg(lastCheckedAt).arg(Utils.getTimerString(autoTryTimerInSecs))
|
||||||
|
else
|
||||||
|
return qsTr("POKT & Infura down. Token balances cannot be retrieved. Retrying in %1.").arg(Utils.getTimerString(autoTryTimerInSecs))
|
||||||
|
}
|
||||||
|
else if(chainIdsDown.length > 0) {
|
||||||
|
updateTimer.restart()
|
||||||
|
if(chainIdsDown.length > 2) {
|
||||||
|
return qsTr("POKT & Infura down for <a href='#'>multiple chains </a>. Token balances for those chains cannot be retrieved. Retrying in %1.").arg(Utils.getTimerString(autoTryTimerInSecs))
|
||||||
|
}
|
||||||
|
else if(chainIdsDown.length === 1) {
|
||||||
|
return qsTr("POKT & Infura down for %1. %1 token balances are as of %2. Retrying in %3.").arg(jointChainIdString).arg(lastCheckedAt).arg(Utils.getTimerString(autoTryTimerInSecs))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return qsTr("POKT & Infura down for %1. %1 token balances cannot be retrieved. Retrying in %2.").arg(jointChainIdString).arg(Utils.getTimerString(autoTryTimerInSecs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return ""
|
||||||
|
case Constants.ConnectionStatus.Retrying:
|
||||||
|
return qsTr("Retrying connection to Pocket Network (POKT).")
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onLinkActivated: {
|
||||||
|
let tootipMessage = qsTr("Pocket Network (POKT) & Infura are currently both unavailable for %1. Balances for those chains are as of %2.").arg(jointChainIdString).arg(lastCheckedAt)
|
||||||
|
toolTip.show(tootipMessage, 3000)
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusToolTip {
|
||||||
|
id: toolTip
|
||||||
|
orientation: StatusToolTip.Orientation.Bottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionWarnings {
|
||||||
|
id: walletCollectiblesConnectionBanner
|
||||||
|
objectName: "walletCollectiblesConnectionBanner"
|
||||||
|
Layout.fillWidth: true
|
||||||
|
websiteDown: Constants.walletConnections.collectibles
|
||||||
|
text: {
|
||||||
|
switch(connectionState) {
|
||||||
|
case Constants.ConnectionStatus.Success:
|
||||||
|
return qsTr("Opensea connection successful")
|
||||||
|
case Constants.ConnectionStatus.Failure:
|
||||||
|
updateTimer.restart()
|
||||||
|
if(withCache){
|
||||||
|
return qsTr("Opensea down. Collectibles are as of %1. Retrying in %2.").arg(lastCheckedAt).arg(Utils.getTimerString(autoTryTimerInSecs))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return qsTr("Opensea down. Retrying in %1.").arg(Utils.getTimerString(autoTryTimerInSecs))
|
||||||
|
}
|
||||||
|
case Constants.ConnectionStatus.Retrying:
|
||||||
|
return qsTr("Retrying connection to Opensea...")
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ConnectionWarnings {
|
||||||
|
id: walletMarketConnectionBanner
|
||||||
|
objectName: "walletMarketConnectionBanner"
|
||||||
|
Layout.fillWidth: true
|
||||||
|
websiteDown: Constants.walletConnections.market
|
||||||
|
text: {
|
||||||
|
switch(connectionState) {
|
||||||
|
case Constants.ConnectionStatus.Success:
|
||||||
|
return qsTr("CryptoCompare and CoinGecko connection successful")
|
||||||
|
case Constants.ConnectionStatus.Failure: {
|
||||||
|
updateTimer.restart()
|
||||||
|
if(withCache) {
|
||||||
|
return qsTr("CryptoCompare and CoinGecko down. Market values are as of %1. Retrying in %2.").arg(lastCheckedAt).arg(Utils.getTimerString(autoTryTimerInSecs))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return qsTr("CryptoCompare and CoinGecko down. Market values cannot be retrieved. Trying again in %1.").arg(Utils.getTimerString(autoTryTimerInSecs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Constants.ConnectionStatus.Retrying:
|
||||||
|
return qsTr("Retrying connection to CryptoCompare and CoinGecko...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
import QtQuick 2.14
|
||||||
|
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
|
||||||
|
import utils 1.0
|
||||||
|
|
||||||
|
ModuleWarning {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string websiteDown
|
||||||
|
property int connectionState: -1
|
||||||
|
property int autoTryTimerInSecs: 0
|
||||||
|
property var chainIdsDown: []
|
||||||
|
property bool completelyDown: false
|
||||||
|
property string lastCheckedAt
|
||||||
|
property bool withCache: false
|
||||||
|
property Timer updateTimer: Timer {
|
||||||
|
interval: 1000
|
||||||
|
repeat: true
|
||||||
|
onTriggered: {
|
||||||
|
if (root.autoTryTimerInSecs === 0) {
|
||||||
|
stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
root.autoTryTimerInSecs = root.autoTryTimerInSecs - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateBanner() {
|
||||||
|
hide()
|
||||||
|
if (connectionState === Constants.ConnectionStatus.Failure)
|
||||||
|
show()
|
||||||
|
else
|
||||||
|
showFor(3000)
|
||||||
|
}
|
||||||
|
|
||||||
|
type: connectionState === Constants.ConnectionStatus.Success ? ModuleWarning.Success : ModuleWarning.Danger
|
||||||
|
buttonText: connectionState === Constants.ConnectionStatus.Failure ? qsTr("Retry now") : ""
|
||||||
|
|
||||||
|
onClicked: appMain.rootStore.retryConnection(websiteDown)
|
||||||
|
onCloseClicked: hide()
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: appMain.rootStore.networkConnectionModuleInst
|
||||||
|
function onNetworkConnectionStatusUpdate(website: string, completelyDown: bool, connectionState: int, chainIds: string, lastCheckedAt: int, timeToAutoRetryInSecs: int, withCache: bool) {
|
||||||
|
if (website === websiteDown) {
|
||||||
|
root.connectionState = connectionState
|
||||||
|
root.autoTryTimerInSecs = timeToAutoRetryInSecs
|
||||||
|
root.chainIdsDown = chainIds.split(";")
|
||||||
|
root.completelyDown = completelyDown
|
||||||
|
root.withCache = withCache
|
||||||
|
root.lastCheckedAt = LocaleUtils.formatDateTime(new Date(lastCheckedAt*1000))
|
||||||
|
root.updateBanner()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,4 +22,5 @@ EditCroppedImagePanel 1.0 EditCroppedImagePanel.qml
|
||||||
NoImageUploadedPanel 1.0 NoImageUploadedPanel.qml
|
NoImageUploadedPanel 1.0 NoImageUploadedPanel.qml
|
||||||
StatusAssetSelector 1.0 StatusAssetSelector.qml
|
StatusAssetSelector 1.0 StatusAssetSelector.qml
|
||||||
AcceptRejectOptionsButtonsPanel 1.0 AcceptRejectOptionsButtonsPanel.qml
|
AcceptRejectOptionsButtonsPanel 1.0 AcceptRejectOptionsButtonsPanel.qml
|
||||||
DidYouKnowSplashScreen 1.0 DidYouKnowSplashScreen.qml
|
DidYouKnowSplashScreen 1.0 DidYouKnowSplashScreen.qml
|
||||||
|
ConnectionWarnings 1.0 ConnectionWarnings.qml
|
||||||
|
|
|
@ -676,6 +676,19 @@ QtObject {
|
||||||
readonly property string seedWalletType: "seed"
|
readonly property string seedWalletType: "seed"
|
||||||
readonly property string generatedWalletType: "generated"
|
readonly property string generatedWalletType: "generated"
|
||||||
|
|
||||||
|
|
||||||
|
readonly property QtObject walletConnections: QtObject {
|
||||||
|
readonly property string collectibles: "collectibles"
|
||||||
|
readonly property string blockchains: "blockchains"
|
||||||
|
readonly property string market: "market"
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ConnectionStatus {
|
||||||
|
Success = 0,
|
||||||
|
Failure = 1,
|
||||||
|
Retrying = 2
|
||||||
|
}
|
||||||
|
|
||||||
readonly property string dummyText: "Dummy"
|
readonly property string dummyText: "Dummy"
|
||||||
readonly property int dummyModelItems: 25
|
readonly property int dummyModelItems: 25
|
||||||
|
|
||||||
|
|
|
@ -603,6 +603,20 @@ QtObject {
|
||||||
return 34
|
return 34
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getTimerString(timeInSecs) {
|
||||||
|
let result = ""
|
||||||
|
const hour = Math.floor(timeInSecs/60/60)
|
||||||
|
const mins = Math.floor(timeInSecs/60)
|
||||||
|
const secs = Math.floor(timeInSecs%60)
|
||||||
|
if(hour > 0 )
|
||||||
|
result += qsTr(" %n hour(s) ", "", hour)
|
||||||
|
if(mins > 0)
|
||||||
|
result += qsTr(" %n min(s) ", "", mins)
|
||||||
|
if(secs > 0)
|
||||||
|
result += qsTr(" %n sec(s) ", "", secs)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// Leave this function at the bottom of the file as QT Creator messes up the code color after this
|
// Leave this function at the bottom of the file as QT Creator messes up the code color after this
|
||||||
function isPunct(c) {
|
function isPunct(c) {
|
||||||
return /(!|\@|#|\$|%|\^|&|\*|\(|\)|\+|\||-|=|\\|{|}|[|]|"|;|'|<|>|\?|,|\.|\/)/.test(c)
|
return /(!|\@|#|\$|%|\^|&|\*|\(|\)|\+|\||-|=|\\|{|}|[|]|"|;|'|<|>|\?|,|\.|\/)/.test(c)
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 7bc03e22f724408cf8ac14117915f72ef7888d88
|
Subproject commit a3f1a84d291749d32408f340d331877555cee5c1
|
Loading…
Reference in New Issue