refactor(about): refactor version fetch

This commit is contained in:
Jonathan Rainville 2021-12-20 13:21:21 -05:00 committed by Sale Djenic
parent b2546fa709
commit 13543ae14f
18 changed files with 303 additions and 105 deletions

View File

@ -20,7 +20,6 @@ import ../../app_service/service/dapp_permissions/service as dapp_permissions_se
import ../../app_service/service/mnemonic/service as mnemonic_service
import ../../app_service/service/privacy/service as privacy_service
import ../../app_service/service/provider/service as provider_service
import ../../app_service/service/ens/service as ens_service
import ../../app_service/service/profile/service as profile_service
import ../../app_service/service/settings/service as settings_service
import ../../app_service/service/stickers/service as stickers_service
@ -85,7 +84,6 @@ type
walletAccountService: wallet_account_service.Service
bookmarkService: bookmark_service.Service
dappPermissionsService: dapp_permissions_service.Service
ensService: ens_service.Service
providerService: provider_service.Service
profileService: profile_service.Service
settingsService: settings_service.Service
@ -166,13 +164,12 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
result.networkService,
result.chatService
)
result.aboutService = about_service.newService()
result.aboutService = about_service.newService(statusFoundation.status.events, statusFoundation.threadpool, result.settingsService)
result.dappPermissionsService = dapp_permissions_service.newService()
result.languageService = language_service.newService()
result.mnemonicService = mnemonic_service.newService()
result.privacyService = privacy_service.newService()
result.ensService = ens_service.newService()
result.providerService = provider_service.newService(result.dappPermissionsService, result.settingsService, result.ensService)
result.providerService = provider_service.newService(result.dappPermissionsService, result.settingsService)
result.savedAddressService = saved_address_service.newService(statusFoundation.status.events)
# Modules
@ -241,7 +238,6 @@ proc delete*(self: AppController) =
self.activityCenterService.delete
self.dappPermissionsService.delete
self.providerService.delete
self.ensService.delete
self.nodeConfigurationService.delete
self.settingsService.delete
self.stickersService.delete
@ -277,7 +273,6 @@ proc load(self: AppController) =
self.bookmarkService.init()
self.tokenService.init()
self.dappPermissionsService.init()
self.ensService.init()
self.providerService.init()
self.walletAccountService.init()
self.transactionService.init()
@ -286,6 +281,7 @@ proc load(self: AppController) =
self.networkService.init()
self.activityCenterService.init()
self.savedAddressService.init()
self.aboutService.init()
let pubKey = self.settingsService.getPublicKey()
singletonInstance.localAccountSensitiveSettings.setFileName(pubKey)

View File

@ -74,7 +74,7 @@ proc newModule*[T](
profileService: profile_service.ServiceInterface,
settingsService: settings_service.ServiceInterface,
contactsService: contacts_service.Service,
aboutService: about_service.ServiceInterface,
aboutService: about_service.Service,
dappPermissionsService: dapp_permissions_service.ServiceInterface,
languageService: language_service.ServiceInterface,
mnemonicService: mnemonic_service.ServiceInterface,

View File

@ -1,29 +1,39 @@
import ./controller_interface
import eventemitter
import io_interface
import ../../../../../app_service/service/about/service as about_service
# import ./item as item
export controller_interface
type
Controller* = ref object of controller_interface.AccessInterface
delegate: io_interface.AccessInterface
aboutService: about_service.ServiceInterface
events: EventEmitter
aboutService: about_service.Service
proc newController*(delegate: io_interface.AccessInterface, aboutService: about_service.ServiceInterface): Controller =
proc newController*(
delegate: io_interface.AccessInterface,
events: EventEmitter,
aboutService: about_service.Service
): Controller =
result = Controller()
result.delegate = delegate
result.events = events
result.aboutService = aboutService
method delete*(self: Controller) =
discard
method init*(self: Controller) =
discard
self.events.on(SIGNAL_VERSION_FETCHED) do(e: Args):
let args = VersionArgs(e)
self.delegate.versionFetched(args.version)
method getAppVersion*(self: Controller): string =
return self.aboutService.getAppVersion()
method checkForUpdates*(self: Controller) =
self.aboutService.checkForUpdates()
method getNodeVersion*(self: Controller): string =
return self.aboutService.getNodeVersion()

View File

@ -16,6 +16,9 @@ method getAppVersion*(self: AccessInterface): string {.base.} =
method getNodeVersion*(self: AccessInterface): string {.base.} =
raise newException(ValueError, "No implementation available")
method checkForUpdates*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
type
## Abstract class (concept) which must be implemented by object/s used in this
## module.

View File

@ -17,6 +17,12 @@ method getAppVersion*(self: AccessInterface): string {.base.} =
method getNodeVersion*(self: AccessInterface): string {.base.} =
raise newException(ValueError, "No implementation available")
method versionFetched*(self: AccessInterface, version: string) {.base.} =
raise newException(ValueError, "No implementation available")
method checkForUpdates*(self: AccessInterface) {.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.

View File

@ -1,4 +1,5 @@
import NimQml, Tables
import NimQml
import eventemitter
import ./io_interface, ./view, ./controller
import ../io_interface as delegate_interface
@ -16,12 +17,16 @@ type
viewVariant: QVariant
moduleLoaded: bool
proc newModule*(delegate: delegate_interface.AccessInterface, aboutService: about_service.ServiceInterface): Module =
proc newModule*(
delegate: delegate_interface.AccessInterface,
events: EventEmitter,
aboutService: about_service.Service
): Module =
result = Module()
result.delegate = delegate
result.view = newView(result)
result.viewVariant = newQVariant(result.view)
result.controller = controller.newController(result, aboutService)
result.controller = controller.newController(result, events, aboutService)
result.moduleLoaded = false
singletonInstance.engine.setRootContextProperty("aboutModule", result.viewVariant)
@ -31,6 +36,7 @@ method delete*(self: Module) =
method load*(self: Module) =
self.view.load()
self.controller.init()
method isLoaded*(self: Module): bool =
return self.moduleLoaded
@ -44,3 +50,9 @@ method getAppVersion*(self: Module): string =
method getNodeVersion*(self: Module): string =
return self.controller.getNodeVersion()
method checkForUpdates*(self: Module) =
self.controller.checkForUpdates()
method versionFetched*(self: Module, version: string) =
self.view.versionFetched(version)

View File

@ -1,4 +1,4 @@
import NimQml
import NimQml, json
# import ./controller_interface
import ./io_interface
@ -7,6 +7,7 @@ QtObject:
type
View* = ref object of QObject
delegate: io_interface.AccessInterface
newVersion*: string
proc delete*(self: View) =
self.QObject.delete
@ -15,6 +16,11 @@ QtObject:
new(result, delete)
result.QObject.setup
result.delegate = delegate
result.newVersion = $(%*{
"available": false,
"version": "0.0.0",
"url": "about:blank"
})
proc load*(self: View) =
self.delegate.viewDidLoad()
@ -24,3 +30,19 @@ QtObject:
proc nodeVersion*(self: View): string {.slot.} =
return self.delegate.getNodeVersion()
proc newVersionChanged(self: View) {.signal.}
proc versionFetched*(self: View, version: string) =
self.newVersion = version
self.newVersionChanged()
proc checkForUpdates*(self: View) {.slot.} =
self.delegate.checkForUpdates()
proc getNewVersion*(self: View): string {.slot.} =
return self.newVersion
QtProperty[string] newVersion:
read = getNewVersion
notify = newVersionChanged

View File

@ -46,7 +46,7 @@ proc newModule*[T](delegate: T,
settingsService: settings_service.ServiceInterface,
profileService: profile_service.ServiceInterface,
contactsService: contacts_service.Service,
aboutService: about_service.ServiceInterface,
aboutService: about_service.Service,
languageService: language_service.ServiceInterface,
mnemonicService: mnemonic_service.ServiceInterface,
privacyService: privacy_service.ServiceInterface,
@ -65,7 +65,7 @@ proc newModule*[T](delegate: T,
result.languageModule = language_module.newModule(result, languageService)
result.mnemonicModule = mnemonic_module.newModule(result, mnemonicService)
result.privacyModule = privacy_module.newModule(result, privacyService, accountsService)
result.aboutModule = about_module.newModule(result, aboutService)
result.aboutModule = about_module.newModule(result, events, aboutService)
result.advancedModule = advanced_module.newModule(result, settingsService, nodeConfigurationService)
singletonInstance.engine.setRootContextProperty("profileSectionModule", result.viewVariant)

View File

@ -0,0 +1,27 @@
include ../../common/json_utils
include ../../../app/core/tasks/common
type CheckForNewVersionTaskArg = ref object of QObjectTaskArg
proc getLatestVersionJSON(): string =
var version = ""
var url = ""
try:
debug "Getting latest version information"
let latestVersion = getLatestVersion()
version = latestVersion.version
url = latestVersion.url
except Exception as e:
error "Error while getting latest version information", msg = e.msg
result = $(%*{
"version": version,
"url": url
})
const checkForUpdatesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
debug "Check for updates - async"
let arg = decode[CheckForNewVersionTaskArg](argEncoded)
arg.finish(getLatestVersionJSON())

View File

@ -1,11 +1,15 @@
import json, json_serialization, sequtils, chronicles
# import status/statusgo_backend_new/custom_tokens as custom_tokens
import NimQml, json, chronicles
import status/statusgo_backend/settings as status_go_settings
import eventemitter
import ../../../app/core/[main]
import ../../../app/core/tasks/[qt, threadpool]
import ./service_interface, ./dto
import ../settings/service as settings_service
import ../network/types
import status/statusgo_backend_new/about as status_about
import ./update
export service_interface
include async_tasks
logScope:
topics = "settings-service"
@ -13,27 +17,70 @@ logScope:
# This is changed during compilation by reading the VERSION file
const DESKTOP_VERSION {.strdefine.} = "0.0.0"
type
Service* = ref object of ServiceInterface
# profile: Dto
type
VersionArgs* = ref object of Args
version*: string
method delete*(self: Service) =
discard
const SIGNAL_VERSION_FETCHED* = "SIGNAL_VERSION_FETCHED"
proc newService*(): Service =
result = Service()
QtObject:
type
Service* = ref object of QObject
events: EventEmitter
threadpool: ThreadPool
settingsService: settings_service.Service
method init*(self: Service) =
try:
echo "init"
# Forward declaration
proc asyncRequestLatestVersion(self: Service)
except Exception as e:
let errDesription = e.msg
error "error: ", errDesription
return
proc delete*(self: Service) =
discard
method getAppVersion*(self: Service): string =
return DESKTOP_VERSION
proc newService*(
events: EventEmitter,
threadpool: ThreadPool,
settingsService: settings_service.Service
): Service =
result = Service()
result.events = events
result.threadpool = threadpool
result.settingsService = settingsService
method getNodeVersion*(self: Service): string =
return status_go_settings.getWeb3ClientVersion()
proc init*(self: Service) =
# TODO uncomment this once the latest version calls is fixed
# to fix this, you need to re-upload the version and files to IPFS and pin them
# self.asyncRequestLatestVersion()
discard
proc getAppVersion*(self: Service): string =
return DESKTOP_VERSION
proc getNodeVersion*(self: Service): string =
try:
return status_about.getWeb3ClientVersion().result.getStr
except Exception as e:
error "Error getting Node version"
proc asyncRequestLatestVersion(self: Service) =
let networkType = self.settingsService.getCurrentNetwork().toNetworkType()
if networkType != NetworkType.Mainnet: return
let arg = CheckForNewVersionTaskArg(
tptr: cast[ByteAddress](checkForUpdatesTask),
vptr: cast[ByteAddress](self.vptr),
slot: "latestVersionSuccess"
)
self.threadpool.start(arg)
proc checkForUpdates*(self: Service) =
self.asyncRequestLatestVersion()
proc latestVersionSuccess*(self: Service, latestVersionJSON: string) {.slot.} =
let latestVersionObj = parseJSON(latestVersionJSON)
let latestVersion = latestVersionObj{"version"}.getStr()
if latestVersion == "": return
let available = isNewer(DESKTOP_VERSION, latestVersion)
latestVersionObj["available"] = newJBool(available)
self.events.emit(SIGNAL_VERSION_FETCHED,
VersionArgs(version: $(%*latestVersionObj)))

View File

@ -1,22 +0,0 @@
import dto
export dto
type
ServiceInterface* {.pure inheritable.} = ref object of RootObj
## Abstract class for this service access.
method delete*(self: ServiceInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method init*(self: ServiceInterface) {.base.} =
raise newException(ValueError, "No implementation available")
# method getPubKey*(self: ServiceInterface): string {.base.} =
# raise newException(ValueError, "No implementation available")
method getAppVersion*(self: ServiceInterface): string {.base.} =
raise newException(ValueError, "No implementation available")
method getNodeVersion*(self: ServiceInterface): string {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -0,0 +1,55 @@
from stew/base32 import nil
from stew/base58 import nil
import chronicles, httpclient, net
import strutils
import semver
import ../provider/service as provider_service
import ../ens/utils as ens_utils
import status/statusgo_backend_new/ens as status_ens
const APP_UPDATES_ENS* = "desktop.status.eth"
const CHECK_VERSION_TIMEOUT_MS* = 5000
type
VersionInfo* = object
version*: string
url*: string
proc getLatestVersion*(): VersionInfo =
let contentHash = contenthash(APP_UPDATES_ENS)
if contentHash == "":
raise newException(ValueError, "ENS does not have a content hash")
var url: string = ""
let decodedHash = contentHash.decodeENSContentHash()
case decodedHash[0]:
of ENSType.IPFS:
let
base58bytes = base58.decode(base58.BTCBase58, decodedHash[1])
base32Hash = base32.encode(base32.Base32Lower, base58bytes)
url = "https://" & base32Hash & IPFS_GATEWAY
of ENSType.SWARM:
url = "https://" & SWARM_GATEWAY & "/bzz:/" & decodedHash[1]
of ENSType.IPNS:
url = "https://" & decodedHash[1]
else:
warn "Unknown content for", contentHash
raise newException(ValueError, "Unknown content for " & contentHash)
# Read version from folder
let secureSSLContext = newContext()
let client = newHttpClient(sslContext = secureSSLContext, timeout = CHECK_VERSION_TIMEOUT_MS)
result.version = client.getContent(url & "/VERSION").strip()
result.url = url
proc isNewer*(currentVersion, versionToCheck: string): bool =
let lastVersion = parseVersion(versionToCheck)
let currVersion = parseVersion(currentVersion)
result = lastVersion > currVersion

View File

@ -1,29 +0,0 @@
import Tables, json, chronicles
import chronicles
include ../../common/json_utils
import service_interface
import status/statusgo_backend_new/ens as status_go
export service_interface
logScope:
topics = "ens-service"
type
Service* = ref object of ServiceInterface
method delete*(self: Service) =
discard
proc newService*(): Service =
result = Service()
method init*(self: Service) =
discard
method resourceUrl*(self: Service, username: string): (string, string, string) =
try:
let response = status_go.resourceURL(chainId=1, username=username)
return (response.result{"Scheme"}.getStr, response.result{"Host"}.getStr, response.result{"Path"}.getStr)
except Exception as e:
error "Error getting ENS resourceUrl", username=username, exception=e.msg
raise

View File

@ -0,0 +1,69 @@
import Tables, json, chronicles, strutils
import sets
import options
import chronicles, libp2p/[multihash, multicodec, cid]
import nimcrypto
include ../../common/json_utils
import status/statusgo_backend_new/ens as status_go
logScope:
topics = "ens-utils"
type
ENSType* {.pure.} = enum
IPFS,
SWARM,
IPNS,
UNKNOWN
proc getContentHash*(ens: string): Option[string] =
try:
let contentHash = status_go.contenthash(ens)
if contentHash != "":
return some(contentHash)
except Exception as e:
let errDescription = e.msg
error "error: ", errDescription
return none(string)
proc decodeENSContentHash*(value: string): tuple[ensType: ENSType, output: string] =
if value == "":
return (ENSType.UNKNOWN, "")
if value[0..5] == "e40101":
return (ENSType.SWARM, value.split("1b20")[1])
if value[0..7] == "e3010170":
try:
let defaultCodec = parseHexInt("70") #dag-pb
var codec = defaultCodec # no codec specified
var codecStartIdx = 2 # idx of where codec would start if it was specified
# handle the case when starts with 0xe30170 instead of 0xe3010170
if value[2..5] == "0101":
codecStartIdx = 6
codec = parseHexInt(value[6..7])
elif value[2..3] == "01" and value[4..5] != "12":
codecStartIdx = 4
codec = parseHexInt(value[4..5])
# strip the info we no longer need
var multiHashStr = value[codecStartIdx + 2..<value.len]
# The rest of the hash identifies the multihash algo, length, and digest
# More info: https://multiformats.io/multihash/
# 12 = identifies sha2-256 hash
# 20 = multihash length = 32
# ...rest = multihash digest
let
multiHash = MultiHash.init(nimcrypto.fromHex(multiHashStr)).get()
decoded = Cid.init(CIDv0, MultiCodec.codec(codec), multiHash).get()
return (ENSType.IPFS, $decoded)
except Exception as e:
error "Error decoding ENS contenthash", hash=value, exception=e.msg
raise
if value[0..8] == "e50101700":
return (ENSType.IPNS, parseHexStr(value[12..value.len-1]))
return (ENSType.UNKNOWN, "")

View File

@ -5,7 +5,7 @@ import strutils
include ../../common/json_utils
import ../dapp_permissions/service as dapp_permissions_service
import ../settings/service_interface as settings_service
import ../ens/service as ens_service
import ../ens/utils as ens_utils
import service_interface
import status/statusgo_backend_new/permissions as status_go_permissions
import status/statusgo_backend_new/accounts as status_go_accounts
@ -86,24 +86,19 @@ proc toAPIRequest(message: string): APIRequest =
hostname: data{"hostname"}.getStr()
)
type
Service* = ref object of service_interface.ServiceInterface
dappPermissionsService: dapp_permissions_service.ServiceInterface
settingsService: settings_service.ServiceInterface
ensService: ens_service.ServiceInterface
method delete*(self: Service) =
discard
proc newService*(dappPermissionsService: dapp_permissions_service.ServiceInterface,
settingsService: settings_service.ServiceInterface,
ensService: ens_service.ServiceInterface): Service =
settingsService: settings_service.ServiceInterface): Service =
result = Service()
result.dappPermissionsService = dappPermissionsService
result.settingsService = settingsService
result.ensService = ensService
method init*(self: Service) =
discard
@ -299,11 +294,11 @@ method postMessage*(self: Service, message: string): string =
else: """{"type":"TODO-IMPLEMENT-THIS"}""" ##################### TODO:
method ensResourceURL*(self: Service, ens: string, url: string): (string, string, string, string, bool) =
let contentHash = self.ensService.getContentHash(ens)
let contentHash = ens_utils.getContentHash(ens)
if contentHash.isNone(): # ENS does not have a content hash
return (url, url, HTTPS_SCHEME, "", false)
let decodedHash = self.ensService.decodeENSContentHash(contentHash.get())
let decodedHash = ens_utils.decodeENSContentHash(contentHash.get())
case decodedHash[0]:
of ENSType.IPFS:

View File

@ -10,6 +10,7 @@ QtObject {
property var mainModuleInst: mainModule
property var profileModuleInst: profileModule
property var aboutModuleInst: aboutModule
// Not Refactored Yet
// property var chatsModelInst: chatsModel

View File

@ -32,14 +32,20 @@ Item {
anchors.fill: parent
property alias appLayout: appLayout
property RootStore rootStore: RootStore { }
// set from main.qml
property var sysPalette
// Not Refactored Yet
property var newVersionJSON: ({}) //JSON.parse(utilsModel.newVersion)
property var newVersionJSON: {
try {
return JSON.parse(rootStore.aboutModuleInst.newVersion)
} catch (e) {
console.error("Error parsing version data", e)
return {}
}
}
property bool profilePopupOpened: false
// Not Refactored Yet
// property bool networkGuarded: profileModel.network.current === Constants.networkMainnet || (profileModel.network.current === Constants.networkRopsten && localAccountSensitiveSettings.stickersEnsRopsten)
property RootStore rootStore: RootStore { }
signal openContactsPopup()