feat: check for updates (using status-go)

This commit is contained in:
Richard Ramos 2022-03-03 17:00:52 -04:00
parent 04037d1d64
commit 75a3ff858c
17 changed files with 120 additions and 148 deletions

View File

@ -38,6 +38,7 @@ type SignalType* {.pure.} = enum
HistoryArchivesSeeding = "community.historyArchivesSeeding"
HistoryArchivesUnseeded = "community.historyArchivesUnseeded"
HistoryArchiveDownloaded = "community.historyArchiveDownloaded"
UpdateAvailable = "update.available"
Unknown
proc event*(self:SignalType):string =

View File

@ -0,0 +1,17 @@
import json
import base
import signal_type
type UpdateAvailableSignal* = ref object of Signal
available*: bool
version*: string
url*: string
proc fromEvent*(T: type UpdateAvailableSignal, jsonSignal: JsonNode): UpdateAvailableSignal =
result = UpdateAvailableSignal()
result.signalType = SignalType.UpdateAvailable
if jsonSignal["event"].kind != JNull:
result.available = jsonSignal["event"]["available"].getBool()
result.version = jsonSignal["event"]["version"].getStr()
result.url = jsonSignal["event"]["url"].getStr()

View File

@ -92,6 +92,7 @@ QtObject:
of SignalType.HistoryArchivesSeeding: HistoryArchivesSignal.historyArchivesSeedingFromEvent(jsonSignal)
of SignalType.HistoryArchivesUnseeded: HistoryArchivesSignal.historyArchivesUnseededFromEvent(jsonSignal)
of SignalType.HistoryArchiveDownloaded: HistoryArchivesSignal.historyArchiveDownloadedFromEvent(jsonSignal)
of SignalType.UpdateAvailable: UpdateAvailableSignal.fromEvent(jsonSignal)
else: Signal()
result.signalType = signalType

View File

@ -1,7 +1,7 @@
{.used.}
import ./remote_signals/[base, chronicles_logs, community, discovery_summary, envelope, expired, mailserver, messages,
peerstats, signal_type, stats, wallet, whisper_filter, keycard]
peerstats, signal_type, stats, wallet, whisper_filter, keycard, update_available]
export base, chronicles_logs, community, discovery_summary, envelope, expired, mailserver, messages, peerstats,
signal_type, stats, wallet, whisper_filter, keycard
signal_type, stats, wallet, whisper_filter, keycard, update_available

View File

@ -24,7 +24,7 @@ proc delete*(self: Controller) =
proc init*(self: Controller) =
self.events.on(SIGNAL_VERSION_FETCHED) do(e: Args):
let args = VersionArgs(e)
self.delegate.versionFetched(args.version)
self.delegate.versionFetched(args.available, args.version, args.url)
proc getAppVersion*(self: Controller): string =
return self.aboutService.getAppVersion()

View File

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

View File

@ -55,5 +55,5 @@ method getNodeVersion*(self: Module): string =
method checkForUpdates*(self: Module) =
self.controller.checkForUpdates()
method versionFetched*(self: Module, version: string) =
self.view.versionFetched(version)
method versionFetched*(self: Module, available: bool, version: string, url: string) =
self.view.versionFetched(available, version, url)

View File

@ -7,7 +7,6 @@ QtObject:
type
View* = ref object of QObject
delegate: io_interface.AccessInterface
newVersion*: string
fetching*: bool
proc delete*(self: View) =
@ -18,14 +17,6 @@ QtObject:
result.QObject.setup
result.delegate = delegate
result.fetching = false
result.newVersion = $(%*{
"available": false,
"version": "",
"url": ""
})
proc load*(self: View) =
self.delegate.viewDidLoad()
proc getCurrentVersion*(self: View): string {.slot.} =
return self.delegate.getAppVersion()
@ -33,31 +24,26 @@ QtObject:
proc nodeVersion*(self: View): string {.slot.} =
return self.delegate.getNodeVersion()
proc appVersionFetched(self: View) {.signal.}
proc appVersionFetched*(self: View, available: bool, version: string, url: string) {.signal.}
proc fetchingChanged(self: View) {.signal.}
proc versionFetched*(self: View, version: string) =
self.newVersion = version
proc versionFetched*(self: View, available: bool, version: string, url: string) =
self.fetching = false
self.fetchingChanged()
self.appVersionFetched()
self.appVersionFetched(available, version, url)
proc checkForUpdates*(self: View) {.slot.} =
self.fetching = true
self.fetchingChanged()
self.delegate.checkForUpdates()
proc getNewVersion*(self: View): string {.slot.} =
return self.newVersion
QtProperty[string] newVersion:
read = getNewVersion
notify = appVersionFetched
proc getFetching*(self: View): bool {.slot.} =
return self.fetching
QtProperty[bool] fetching:
read = getFetching
notify = fetchingChanged
proc load*(self: View) =
self.delegate.viewDidLoad()

View File

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

View File

@ -1,22 +1,29 @@
import NimQml, json, chronicles
import ../../../app/core/eventemitter
import ../../../app/core/tasks/[qt, threadpool]
import ../../../constants
import ../settings/service as settings_service
import ../network/types
import ../../../app/core/eventemitter
import ../../../app/core/tasks/[qt, threadpool]
import ../../../app/core/signals/types as signal_types
import ../../../backend/backend
import ./update
include async_tasks
import ../../../backend/about as status_about
import ../../../constants
logScope:
topics = "about-service"
# This is changed during compilation by reading the VERSION file
const DESKTOP_VERSION {.strdefine.} = "0.0.0"
const APP_UPDATES_ENS* = "desktop.status.eth"
type
VersionArgs* = ref object of Args
available*: bool
version*: string
url*: string
# Signals which may be emitted by this service:
const SIGNAL_VERSION_FETCHED* = "versionFetched"
@ -28,9 +35,6 @@ QtObject:
threadpool: ThreadPool
settingsService: settings_service.Service
# Forward declaration
proc asyncRequestLatestVersion(self: Service)
proc delete*(self: Service) =
self.QObject.delete
@ -43,12 +47,6 @@ QtObject:
result.events = events
result.threadpool = threadpool
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
@ -58,29 +56,19 @@ QtObject:
except Exception as e:
error "Error getting Node version"
proc emitSignal(self: Service, versionJsonObj: JsonNode) =
self.events.emit(SIGNAL_VERSION_FETCHED, VersionArgs(version: $versionJsonObj))
proc asyncRequestLatestVersion(self: Service) =
let arg = QObjectTaskArg(
tptr: cast[ByteAddress](checkForUpdatesTask),
vptr: cast[ByteAddress](self.vptr),
slot: "latestVersionSuccess"
)
self.threadpool.start(arg)
proc checkForUpdates*(self: Service) =
self.asyncRequestLatestVersion()
try:
discard status_about.checkForUpdates(types.Mainnet, APP_UPDATES_ENS, DESKTOP_VERSION)
except Exception as e:
error "Error checking for updates", msg=e.msg
proc latestVersionSuccess*(self: Service, latestVersionJSON: string) {.slot.} =
var latestVersionObj = parseJSON(latestVersionJSON)
proc init*(self: Service) =
self.events.on(SignalType.UpdateAvailable.event) do(e: Args):
var updateSignal = UpdateAvailableSignal(e)
self.events.emit(SIGNAL_VERSION_FETCHED, VersionArgs(
available: updateSignal.available,
version: updateSignal.version,
url: updateSignal.url))
var newVersionAvailable = false
let latestVersion = latestVersionObj{"version"}.getStr()
if(latestVersion.len > 0):
newVersionAvailable = isNewer(DESKTOP_VERSION, latestVersion)
latestVersionObj["available"] = newJBool(newVersionAvailable)
self.emitSignal(latestVersionObj)
self.checkForUpdates()

View File

@ -1,32 +0,0 @@
import json, chronicles, httpclient, net, options
import strutils
import semver
import ../../../backend/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 response = status_ens.resourceUrl(chainId=1, username=APP_UPDATES_ENS)
let host = response.result{"Host"}.getStr
if host == "":
raise newException(ValueError, "ENS does not have a content hash")
let url = "https://" & host & response.result{"Path"}.getStr
# 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

9
src/backend/about.nim Normal file
View File

@ -0,0 +1,9 @@
import json
import ./core
import response_type
export response_type
proc checkForUpdates*(chainId: int, ensAddress: string, currVersion: string): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [chainId, ensAddress, currVersion]
result = callPrivateRPC("updates_check", payload)

View File

@ -9,6 +9,20 @@ QtObject {
property var communitiesModuleInst: communitiesModule
property var observedCommunity: communitiesModuleInst.observedCommunity
property bool newVersionAvailable: false
property string latestVersion
property string downloadURL
function setLatestVersionInfo(newVersionAvailable, latestVersion, downloadURL) {
root.newVersionAvailable = newVersionAvailable;
root.latestVersion = latestVersion;
root.downloadURL = downloadURL;
}
function resetLastVersion(){
root.newVersionAvailable = false
}
property AppSearchStore appSearchStore: AppSearchStore {
appSearchModule: root.mainModuleInst.appSearchModule
}

View File

@ -43,21 +43,14 @@ Item {
property RootStore rootStore: RootStore { }
// set from main.qml
property var sysPalette
property var newVersionJSON: {
try {
return JSON.parse(rootStore.aboutModuleInst.newVersion)
} catch (e) {
console.error("Error parsing version data", e)
return {}
}
}
signal openContactsPopup()
Connections {
target: rootStore.aboutModuleInst
onAppVersionFetched: {
if (!newVersionJSON.available) {
rootStore.setLatestVersionInfo(available, version, url);
if (!available) {
versionUpToDate.show()
} else {
versionWarning.show()
@ -81,10 +74,10 @@ Item {
onOpenDownloadModalRequested: {
const downloadPage = downloadPageComponent.createObject(appMain,
{
newVersionAvailable: newVersionJSON.available,
downloadURL: newVersionJSON.url,
newVersionAvailable: available,
downloadURL: url,
currentVersion: rootStore.profileSectionStore.getCurrentVersion(),
newVersion: newVersionJSON.version
newVersion: version
})
return downloadPage
}
@ -414,13 +407,16 @@ Item {
id: versionWarning
width: parent.width
height: 32
visible: !!newVersionJSON.available
visible: appMain.rootStore.newVersionAvailable
color: Style.current.green
btnWidth: 100
text: qsTr("A new version of Status (%1) is available").arg(newVersionJSON.version)
text: qsTr("A new version of Status (%1) is available").arg(appMain.rootStore.latestVersion)
btnText: qsTr("Download")
onClick: function(){
Global.openDownloadModal()
Global.openDownloadModal(appMain.rootStore.newVersionAvailable, appMain.rootStore.latestVersion, appMain.rootStore.downloadURL)
}
onClosed: {
appMain.rootStore.resetLastVersion();
}
function show() {

View File

@ -10,20 +10,23 @@ import "./"
Rectangle {
id: root
height: visible ? 32 : 0
implicitHeight: height
color: Style.current.red
property string text: ""
property string btnText: ""
property int btnWidth: 58
property bool closing: false
property var onClick: function() {}
signal closed()
function close() {
closeBtn.clicked(null)
closed();
}
signal closed
Row {
spacing: Style.current.halfPadding
anchors.horizontalCenter: parent.horizontalCenter
@ -89,10 +92,21 @@ Rectangle {
id: closeBtn
anchors.fill: closeImg
cursorShape: Qt.PointingHandCursor
onClicked: ParallelAnimation {
onClicked: {
closing = true
}
}
ParallelAnimation {
running: closing
PropertyAnimation { target: root; property: "visible"; to: false; }
PropertyAnimation { target: root; property: "y"; to: -1 * root.height }
onFinished: root.closed()
onRunningChanged: {
if(!running){
closing = false;
root.y = 0;
root.closed();
}
}
}
}

View File

@ -15,6 +15,9 @@ Item {
property alias tooltipUnder: copyToClipboardBtn.tooltipUnder
property var store
property alias textFont: name.font
property alias textColor: name.color
id: root
width: parent.width
height: name.height

View File

@ -19,7 +19,8 @@ QtObject {
signal openImagePopup(var image, var contextMenu)
signal openLinkInBrowser(string link)
signal openChooseBrowserPopup(string link)
signal openDownloadModalRequested()
signal openPopupRequested(var popupComponent, var params)
signal openDownloadModalRequested(bool available, string version, string url)
signal settingsLoaded()
signal openBackUpSeedPopup()
signal openCreateChatView()
@ -40,8 +41,8 @@ QtObject {
return popup;
}
function openDownloadModal(){
openDownloadModalRequested();
function openDownloadModal(available, version, url){
openDownloadModalRequested(available, version, url);
}
function changeAppSectionBySectionType(sectionType, subsection = 0) {