[GODT-274] GUI changes for autoupdates

[GODT-275] Add enable/disable auto updates GUI option

Refactor Updater module
GODT-805 Changed manual update information bar layout
GODT-806, GODT-875 Change update dialogs
Refactor InformationBar
This commit is contained in:
Alexander Bilyak
2020-11-10 08:09:17 +00:00
committed by James Houlahan
parent b7b2297635
commit 98ab794f13
34 changed files with 1069 additions and 612 deletions

View File

@ -193,7 +193,7 @@ Changelog [format](http://keepachangelog.com/en/1.0.0/)
* GODT-682 Persistent anonymous API cookies for Import-Export. * GODT-682 Persistent anonymous API cookies for Import-Export.
* GODT-357 Use go-message to make a better message parser. * GODT-357 Use go-message to make a better message parser.
* GODT-720 Time measurement of progress for Import-Export. * GODT-720 Time measurement of progress for Import-Export.
* GODT-693 Launcher * GODT-693 Launcher.
### Changed ### Changed
* GODT-511 User agent format changed. * GODT-511 User agent format changed.

View File

@ -27,6 +27,7 @@ import (
"github.com/ProtonMail/proton-bridge/internal/config/settings" "github.com/ProtonMail/proton-bridge/internal/config/settings"
"github.com/ProtonMail/proton-bridge/internal/constants" "github.com/ProtonMail/proton-bridge/internal/constants"
"github.com/ProtonMail/proton-bridge/internal/frontend" "github.com/ProtonMail/proton-bridge/internal/frontend"
"github.com/ProtonMail/proton-bridge/internal/frontend/types"
"github.com/ProtonMail/proton-bridge/internal/imap" "github.com/ProtonMail/proton-bridge/internal/imap"
"github.com/ProtonMail/proton-bridge/internal/smtp" "github.com/ProtonMail/proton-bridge/internal/smtp"
"github.com/ProtonMail/proton-bridge/internal/updater" "github.com/ProtonMail/proton-bridge/internal/updater"
@ -119,19 +120,50 @@ func run(b *base.Base, c *cli.Context) error { // nolint[funlen]
b, b,
) )
b.Updater.Watch( // Watch for updates routine
time.Hour, go func() {
func(update updater.VersionInfo) error { ticker := time.NewTicker(time.Hour)
if !b.Settings.GetBool(settings.AutoUpdateKey) {
return f.NotifyManualUpdate(update)
}
return b.Updater.InstallUpdate(update) for {
}, checkAndHandleUpdate(b.Updater, f, b.Settings.GetBool(settings.AutoUpdateKey))
func(err error) { <-ticker.C
logrus.WithError(err).Error("An error occurred while watching for updates") }
}, }()
)
return f.Loop() return f.Loop()
} }
func checkAndHandleUpdate(u types.Updater, f frontend.Frontend, autoUpdate bool) {
version, err := u.Check()
if err != nil {
logrus.WithError(err).Error("An error occurred while checking for updates")
f.NotifySilentUpdateError(err)
return
}
if !u.IsUpdateApplicable(version) {
logrus.Debug("No need to update")
return
}
logrus.WithField("version", version.Version).Info("An update is available")
if !autoUpdate {
f.NotifyManualUpdate(version, u.CanInstall(version))
return
}
if !u.CanInstall(version) {
logrus.Info("A manual update is required")
f.NotifySilentUpdateError(updater.ErrManualUpdateRequired)
return
}
if err := u.InstallUpdate(version); err != nil {
logrus.WithError(err).Error("An error occurred while silent installing updates")
f.NotifySilentUpdateError(err)
return
}
f.NotifySilentUpdateInstalled()
}

View File

@ -26,6 +26,7 @@ import (
"github.com/ProtonMail/proton-bridge/internal/config/settings" "github.com/ProtonMail/proton-bridge/internal/config/settings"
"github.com/ProtonMail/proton-bridge/internal/constants" "github.com/ProtonMail/proton-bridge/internal/constants"
"github.com/ProtonMail/proton-bridge/internal/frontend" "github.com/ProtonMail/proton-bridge/internal/frontend"
"github.com/ProtonMail/proton-bridge/internal/frontend/types"
"github.com/ProtonMail/proton-bridge/internal/importexport" "github.com/ProtonMail/proton-bridge/internal/importexport"
"github.com/ProtonMail/proton-bridge/internal/updater" "github.com/ProtonMail/proton-bridge/internal/updater"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -59,25 +60,57 @@ func run(b *base.Base, c *cli.Context) error {
frontendMode, frontendMode,
b.CrashHandler, b.CrashHandler,
b.Locations, b.Locations,
b.Settings,
b.Listener, b.Listener,
b.Updater, b.Updater,
ie, ie,
b, b,
) )
b.Updater.Watch( // Watch for updates routine
time.Hour, go func() {
func(update updater.VersionInfo) error { ticker := time.NewTicker(time.Hour)
if !b.Settings.GetBool(settings.AutoUpdateKey) {
return f.NotifyManualUpdate(update)
}
return b.Updater.InstallUpdate(update) for {
}, checkAndHandleUpdate(b.Updater, f, b.Settings.GetBool(settings.AutoUpdateKey))
func(err error) { <-ticker.C
logrus.WithError(err).Error("An error occurred while watching for updates") }
}, }()
)
return f.Loop() return f.Loop()
} }
func checkAndHandleUpdate(u types.Updater, f frontend.Frontend, autoUpdate bool) {
version, err := u.Check()
if err != nil {
logrus.WithError(err).Error("An error occurred while checking for updates")
f.NotifySilentUpdateError(err)
return
}
if !u.IsUpdateApplicable(version) {
logrus.Debug("No need to update")
return
}
logrus.WithField("version", version.Version).Info("An update is available")
if !autoUpdate {
f.NotifyManualUpdate(version, u.CanInstall(version))
return
}
if !u.CanInstall(version) {
logrus.Info("A manual update is required")
f.NotifySilentUpdateError(updater.ErrManualUpdateRequired)
return
}
if err := u.InstallUpdate(version); err != nil {
logrus.WithError(err).Error("An error occurred while silent installing updates")
f.NotifySilentUpdateError(err)
return
}
f.NotifySilentUpdateInstalled()
}

View File

@ -72,7 +72,7 @@ func (s *Settings) setDefaultValues() {
s.setDefault(NextHeartbeatKey, fmt.Sprintf("%v", time.Now().Unix())) s.setDefault(NextHeartbeatKey, fmt.Sprintf("%v", time.Now().Unix()))
s.setDefault(AllowProxyKey, "true") s.setDefault(AllowProxyKey, "true")
s.setDefault(AutostartKey, "true") s.setDefault(AutostartKey, "true")
s.setDefault(AutoUpdateKey, "false") s.setDefault(AutoUpdateKey, "true")
s.setDefault(ReportOutgoingNoEncKey, "false") s.setDefault(ReportOutgoingNoEncKey, "false")
s.setDefault(LastVersionKey, "") s.setDefault(LastVersionKey, "")
s.setDefault(RolloutKey, fmt.Sprintf("%v", rand.Float64())) s.setDefault(RolloutKey, fmt.Sprintf("%v", rand.Float64()))

View File

@ -226,7 +226,12 @@ WARNING: The CLI is an experimental feature and does not yet cover all functiona
return nil return nil
} }
func (f *frontendCLI) NotifyManualUpdate(update updater.VersionInfo) error { func (f *frontendCLI) NotifyManualUpdate(update updater.VersionInfo, canInstall bool) {
// NOTE: Save the update somewhere so that it can be installed when user chooses "install now". // NOTE: Save the update somewhere so that it can be installed when user chooses "install now".
return nil }
func (f *frontendCLI) NotifySilentUpdateInstalled() {
}
func (f *frontendCLI) NotifySilentUpdateError(err error) {
} }

View File

@ -253,7 +253,12 @@ func (f *frontendCLI) Loop() error {
return nil return nil
} }
func (f *frontendCLI) NotifyManualUpdate(update updater.VersionInfo) error { func (f *frontendCLI) NotifyManualUpdate(update updater.VersionInfo, canInstall bool) {
// NOTE: Save the update somewhere so that it can be installed when user chooses "install now". // NOTE: Save the update somewhere so that it can be installed when user chooses "install now".
return nil }
func (f *frontendCLI) NotifySilentUpdateInstalled() {
}
func (f *frontendCLI) NotifySilentUpdateError(err error) {
} }

View File

@ -40,7 +40,9 @@ var (
// Frontend is an interface to be implemented by each frontend type (cli, gui, html). // Frontend is an interface to be implemented by each frontend type (cli, gui, html).
type Frontend interface { type Frontend interface {
Loop() error Loop() error
NotifyManualUpdate(update updater.VersionInfo) error NotifyManualUpdate(update updater.VersionInfo, canInstall bool)
NotifySilentUpdateInstalled()
NotifySilentUpdateError(error)
} }
// New returns initialized frontend based on `frontendType`, which can be `cli` or `qt`. // New returns initialized frontend based on `frontendType`, which can be `cli` or `qt`.
@ -123,8 +125,8 @@ func NewImportExport(
buildVersion, buildVersion,
frontendType string, frontendType string,
panicHandler types.PanicHandler, panicHandler types.PanicHandler,
locations *locations.Locations, locations *locations.Locations,
settings *settings.Settings,
eventListener listener.Listener, eventListener listener.Listener,
updater types.Updater, updater types.Updater,
ie *importexport.ImportExport, ie *importexport.ImportExport,
@ -137,6 +139,7 @@ func NewImportExport(
frontendType, frontendType,
panicHandler, panicHandler,
locations, locations,
settings,
eventListener, eventListener,
updater, updater,
ieWrap, ieWrap,
@ -149,8 +152,8 @@ func newIEFrontend(
buildVersion, buildVersion,
frontendType string, frontendType string,
panicHandler types.PanicHandler, panicHandler types.PanicHandler,
locations *locations.Locations, locations *locations.Locations,
settings *settings.Settings,
eventListener listener.Listener, eventListener listener.Listener,
updater types.Updater, updater types.Updater,
ie types.ImportExporter, ie types.ImportExporter,
@ -158,8 +161,25 @@ func newIEFrontend(
) Frontend { ) Frontend {
switch frontendType { switch frontendType {
case "cli": case "cli":
return cliie.New(panicHandler, locations, eventListener, updater, ie, restarter) return cliie.New(
panicHandler,
locations,
eventListener,
updater,
ie,
restarter,
)
default: default:
return qtie.New(version, buildVersion, panicHandler, locations, eventListener, updater, ie, restarter) return qtie.New(
version,
buildVersion,
panicHandler,
locations,
settings,
eventListener,
updater,
ie,
restarter,
)
} }
} }

View File

@ -368,7 +368,7 @@ Dialog {
if ( state == "quit" ) { Qt.quit () } if ( state == "quit" ) { Qt.quit () }
if ( state == "instance exists" ) { Qt.quit () } if ( state == "instance exists" ) { Qt.quit () }
if ( state == "noKeychain" ) { Qt.quit () } if ( state == "noKeychain" ) { Qt.quit () }
if ( state == "checkUpdates" ) { go.runCheckVersion (true) } if ( state == "checkUpdates" ) { }
} }
} }

View File

@ -74,9 +74,7 @@ Item {
rightIcon.text : Style.fa.chevron_circle_right rightIcon.text : Style.fa.chevron_circle_right
rightIcon.font.pointSize : Style.settings.toggleSize * Style.pt rightIcon.font.pointSize : Style.settings.toggleSize * Style.pt
onClicked: { onClicked: {
dialogGlobal.state="checkUpdates" go.checkForUpdates()
dialogGlobal.show()
dialogGlobal.confirmed()
} }
} }
@ -138,7 +136,7 @@ Item {
fontSize : Style.main.fontSize fontSize : Style.main.fontSize
textUnderline : true textUnderline : true
onClicked : { onClicked : {
Qt.openUrlExternally(go.releaseNotesLink) Qt.openUrlExternally(go.updateReleaseNotesLink)
} }
} }
} }

View File

@ -314,50 +314,7 @@ Window {
DialogUpdate { DialogUpdate {
id: dialogUpdate id: dialogUpdate
forceUpdate: root.isOutdateVersion
property string manualLinks : {
var out = ""
var links = go.downloadLink.split("\n")
var l;
for (l in links) {
out += '<a href="%1">%1</a><br>'.arg(links[l])
}
return out
}
title: root.isOutdateVersion ?
qsTr("%1 is outdated", "title of outdate dialog").arg(go.programTitle):
qsTr("%1 update to %2", "title of update dialog").arg(go.programTitle).arg(go.newversion)
introductionText: {
if (root.isOutdateVersion) {
if (go.goos=="linux") {
return qsTr('You are using an outdated version of our software.<br>
Please download and install the latest version to continue using %1.<br><br>
%2',
"Message for force-update in Linux").arg(go.programTitle).arg(dialogUpdate.manualLinks)
} else {
return qsTr('You are using an outdated version of our software.<br>
Please download and install the latest version to continue using %1.<br><br>
You can continue with the update or download and install the new version manually from<br><br>
<a href="%2">%2</a>',
"Message for force-update in Win/Mac").arg(go.programTitle).arg(go.landingPage)
}
} else {
if (go.goos=="linux") {
return qsTr('A new version of Bridge is available.<br>
Check <a href="%1">release notes</a> to learn what is new in %2.<br>
Use your package manager to update or download and install the new version manually from<br><br>
%3',
"Message for update in Linux").arg(go.releaseNotesLink).arg(go.newversion).arg(dialogUpdate.manualLinks)
} else {
return qsTr('A new version of Bridge is available.<br>
Check <a href="%1">release notes</a> to learn what is new in %2.<br>
You can continue with the update or download and install the new version manually from<br><br>
<a href="%3">%3</a>',
"Message for update in Win/Mac").arg(go.releaseNotesLink).arg(go.newversion).arg(go.landingPage)
}
}
}
} }

View File

@ -97,6 +97,25 @@ Item {
} }
} }
ButtonIconText {
id: autoUpdates
text: qsTr("Keep the application up to date", "label for toggle that activates and disables the automatic updates")
leftIcon.text : Style.fa.download
rightIcon {
font.pointSize : Style.settings.toggleSize * Style.pt
text : go.isAutoUpdate!=false ? Style.fa.toggle_on : Style.fa.toggle_off
color : go.isAutoUpdate!=false ? Style.main.textBlue : Style.main.textDisabled
}
Accessible.description: (
go.isAutoUpdate == false ?
qsTr("Enable" , "Click to enable the automatic update of Bridge") :
qsTr("Disable" , "Click to disable the automatic update of Bridge")
) + " " + text
onClicked: {
go.toggleAutoUpdate()
}
}
ButtonIconText { ButtonIconText {
id: advancedSettings id: advancedSettings
property bool isAdvanced : !go.isDefaultPort property bool isAdvanced : !go.isDefaultPort

View File

@ -54,13 +54,15 @@ Item {
onWarningFlagsChanged : { onWarningFlagsChanged : {
if (gui.warningFlags==Style.okInfoBar) { if (gui.warningFlags==Style.okInfoBar) {
go.normalSystray() go.normalSystray()
} else { return
if ((gui.warningFlags & Style.errorInfoBar) == Style.errorInfoBar) {
go.errorSystray()
} else {
go.highlightSystray()
}
} }
if ((gui.warningFlags & Style.errorInfoBar) == Style.errorInfoBar) {
go.errorSystray()
return
}
go.highlightSystray()
} }
// Signals from Go // Signals from Go
@ -112,14 +114,6 @@ Item {
} }
} }
onRunCheckVersion : {
gui.openMainWindow(false)
go.setUpdateState("upToDate")
winMain.dialogGlobal.state="checkUpdates"
winMain.dialogGlobal.show()
go.isNewVersionAvailable(showMessage)
}
onSetUpdateState : { onSetUpdateState : {
// once app is outdated prevent from state change // once app is outdated prevent from state change
if (winMain.updateState != "forceUpdate") { if (winMain.updateState != "forceUpdate") {
@ -134,15 +128,50 @@ Item {
go.silentBubble(2,qsTr("You have the latest version!", "notification", -1)) go.silentBubble(2,qsTr("You have the latest version!", "notification", -1))
} }
onNotifyUpdate : { onNotifyManualUpdate: {
go.setUpdateState("oldVersion")
}
onNotifyManualUpdateRestartNeeded: {
if (!winMain.dialogUpdate.visible) {
gui.openMainWindow(true)
winMain.dialogUpdate.show()
}
go.setUpdateState("updateRestart")
winMain.dialogUpdate.finished(false)
// after manual update - just retart immidiatly
go.setToRestart()
Qt.quit()
}
onNotifyManualUpdateError: {
if (!winMain.dialogUpdate.visible) {
gui.openMainWindow(true)
winMain.dialogUpdate.show()
}
go.setUpdateState("updateError")
winMain.dialogUpdate.finished(true)
}
onNotifyForceUpdate : {
go.setUpdateState("forceUpdate") go.setUpdateState("forceUpdate")
if (!winMain.dialogUpdate.visible) { if (!winMain.dialogUpdate.visible) {
gui.openMainWindow(true) gui.openMainWindow(true)
go.runCheckVersion(false)
winMain.dialogUpdate.show() winMain.dialogUpdate.show()
} }
} }
onNotifySilentUpdateRestartNeeded: {
go.setUpdateState("updateRestart")
gui.openMainWindow(true)
}
onNotifySilentUpdateError: {
go.setUpdateState("updateError")
gui.openMainWindow(true)
}
onNotifyLogout : { onNotifyLogout : {
go.notifyBubble(0, qsTr("Account %1 has been disconnected. Please log in to continue to use the Bridge with this account.").arg(accname) ) go.notifyBubble(0, qsTr("Account %1 has been disconnected. Please log in to continue to use the Bridge with this account.").arg(accname) )
} }
@ -229,10 +258,6 @@ Item {
outgoingNoEncPopup.y = y outgoingNoEncPopup.y = y
} }
onUpdateFinished : {
winMain.dialogUpdate.finished(hasError)
}
onShowCertIssue : { onShowCertIssue : {
winMain.tlsBarState="notOK" winMain.tlsBarState="notOK"
} }
@ -240,14 +265,6 @@ Item {
} }
Timer {
id: checkVersionTimer
repeat : true
triggeredOnStart: false
interval : Style.main.verCheckRepeatTime
onTriggered : go.runCheckVersion(false)
}
function openMainWindow(showAndRise) { function openMainWindow(showAndRise) {
// wait and check until font is loaded // wait and check until font is loaded
while(true){ while(true){
@ -301,10 +318,8 @@ Item {
// start window // start window
gui.openMainWindow(false) gui.openMainWindow(false)
checkVersionTimer.start()
if (go.isShownOnStart) { if (go.isShownOnStart) {
gui.winMain.showAndRise() gui.winMain.showAndRise()
} }
go.runCheckVersion(false)
} }
} }

View File

@ -38,7 +38,7 @@ Item {
property var allMonths : getMonthList(1,12) property var allMonths : getMonthList(1,12)
property var allDays : getDayList(1,31) property var allDays : getDayList(1,31)
property var enums : JSON.parse('{"pathOK":1,"pathEmptyPath":2,"pathWrongPath":4,"pathNotADir":8,"pathWrongPermissions":16,"pathDirEmpty":32,"errUnknownError":0,"errEventAPILogout":1,"errUpdateAPI":2,"errUpdateJSON":3,"errUserAuth":4,"errQApplication":18,"errEmailExportFailed":6,"errEmailExportMissing":7,"errNothingToImport":8,"errEmailImportFailed":12,"errDraftImportFailed":13,"errDraftLabelFailed":14,"errEncryptMessageAttachment":15,"errEncryptMessage":16,"errNoInternetWhileImport":17,"errUnlockUser":5,"errSourceMessageNotSelected":19,"errCannotParseMail":5000,"errWrongLoginOrPassword":5001,"errWrongServerPathOrPort":5002,"errWrongAuthMethod":5003,"errIMAPFetchFailed":5004,"errLocalSourceLoadFailed":1000,"errPMLoadFailed":1001,"errRemoteSourceLoadFailed":1002,"errLoadAccountList":1005,"errExit":1006,"errRetry":1007,"errAsk":1008,"errImportFailed":1009,"errCreateLabelFailed":1010,"errCreateFolderFailed":1011,"errUpdateLabelFailed":1012,"errUpdateFolderFailed":1013,"errFillFolderName":1014,"errSelectFolderColor":1015,"errNoInternet":1016,"folderTypeSystem":"system","folderTypeLabel":"label","folderTypeFolder":"folder","folderTypeExternal":"external","progressInit":"init","progressLooping":"looping","statusNoInternet":"noInternet","statusCheckingInternet":"internetCheck","statusNewVersionAvailable":"oldVersion","statusUpToDate":"upToDate","statusForceUpdate":"forceupdate"}') property var enums : JSON.parse('{"pathOK":1,"pathEmptyPath":2,"pathWrongPath":4,"pathNotADir":8,"pathWrongPermissions":16,"pathDirEmpty":32,"errUnknownError":0,"errEventAPILogout":1,"errUpdateAPI":2,"errUpdateJSON":3,"errUserAuth":4,"errQApplication":18,"errEmailExportFailed":6,"errEmailExportMissing":7,"errNothingToImport":8,"errEmailImportFailed":12,"errDraftImportFailed":13,"errDraftLabelFailed":14,"errEncryptMessageAttachment":15,"errEncryptMessage":16,"errNoInternetWhileImport":17,"errUnlockUser":5,"errSourceMessageNotSelected":19,"errCannotParseMail":5000,"errWrongLoginOrPassword":5001,"errWrongServerPathOrPort":5002,"errWrongAuthMethod":5003,"errIMAPFetchFailed":5004,"errLocalSourceLoadFailed":1000,"errPMLoadFailed":1001,"errRemoteSourceLoadFailed":1002,"errLoadAccountList":1005,"errExit":1006,"errRetry":1007,"errAsk":1008,"errImportFailed":1009,"errCreateLabelFailed":1010,"errCreateFolderFailed":1011,"errUpdateLabelFailed":1012,"errUpdateFolderFailed":1013,"errFillFolderName":1014,"errSelectFolderColor":1015,"errNoInternet":1016,"folderTypeSystem":"system","folderTypeLabel":"label","folderTypeFolder":"folder","folderTypeExternal":"external","progressInit":"init","progressLooping":"looping","statusNoInternet":"noInternet","statusCheckingInternet":"internetCheck","statusNewVersionAvailable":"oldVersion","statusUpToDate":"upToDate","statusForceUpdate":"forceUpdate"}')
IEStyle{} IEStyle{}
@ -103,16 +103,9 @@ Item {
} }
} }
onRunCheckVersion : {
go.setUpdateState(gui.enums.statusUpToDate)
winMain.dialogGlobal.state=gui.enums.statusCheckingInternet
winMain.dialogGlobal.show()
go.isNewVersionAvailable(showMessage)
}
onSetUpdateState : { onSetUpdateState : {
// once app is outdated prevent from state change // once app is outdated prevent from state change
if (winMain.updateState != gui.enums.statusForceUpdate) { if (winMain.updateState != "forceUpdate") {
winMain.updateState = updateState winMain.updateState = updateState
} }
} }
@ -213,13 +206,43 @@ Item {
} }
} }
onNotifyUpdate : { onNotifyManualUpdate: {
go.setUpdateState("forceUpdate") go.setUpdateState("oldVersion")
}
onNotifyManualUpdateRestartNeeded: {
if (!winMain.dialogUpdate.visible) { if (!winMain.dialogUpdate.visible) {
gui.openMainWindow(true)
go.runCheckVersion(false)
winMain.dialogUpdate.show() winMain.dialogUpdate.show()
} }
go.setUpdateState("updateRestart")
winMain.dialogUpdate.finished(false)
// after manual update - just retart immidiatly
go.setToRestart()
Qt.quit()
}
onNotifyManualUpdateError: {
if (!winMain.dialogUpdate.visible) {
winMain.dialogUpdate.show()
}
go.setUpdateState("updateError")
winMain.dialogUpdate.finished(true)
}
onNotifyForceUpdate : {
go.setUpdateState("forceUpdate")
if (!winMain.dialogUpdate.visible) {
winMain.dialogUpdate.show()
}
}
onNotifySilentUpdateRestartNeeded: {
go.setUpdateState("updateRestart")
}
onNotifySilentUpdateError: {
go.setUpdateState("updateError")
} }
onNotifyLogout : { onNotifyLogout : {
@ -382,14 +405,6 @@ Item {
} }
*/ */
Timer {
id: checkVersionTimer
repeat : true
triggeredOnStart: false
interval : Style.main.verCheckRepeatTime
onTriggered : go.runCheckVersion(false)
}
property string areYouSureYouWantToQuit : qsTr("There are incomplete processes - some items are not yet transferred. Do you really want to stop and quit?") property string areYouSureYouWantToQuit : qsTr("There are incomplete processes - some items are not yet transferred. Do you really want to stop and quit?")
// On start // On start
Component.onCompleted : { Component.onCompleted : {
@ -402,9 +417,6 @@ Item {
go.bugNotSent = qsTr("Unable to submit bug report." , "notification", -1) go.bugNotSent = qsTr("Unable to submit bug report." , "notification", -1)
go.bugReportSent = qsTr("Bug report successfully sent." , "notification", -1) go.bugReportSent = qsTr("Bug report successfully sent." , "notification", -1)
go.runCheckVersion(false)
checkVersionTimer.start()
gui.allMonths = getMonthList(1,12) gui.allMonths = getMonthList(1,12)
gui.allMonthsChanged() gui.allMonthsChanged()
} }

View File

@ -341,7 +341,7 @@ Dialog {
if ( state == "toggleAutoStart" ) { go.toggleAutoStart () } if ( state == "toggleAutoStart" ) { go.toggleAutoStart () }
if ( state == "quit" ) { Qt.quit () } if ( state == "quit" ) { Qt.quit () }
if ( state == "instance exists" ) { Qt.quit () } if ( state == "instance exists" ) { Qt.quit () }
if ( state == "checkUpdates" ) { go.runCheckVersion (true) } if ( state == "checkUpdates" ) { }
} }
} }

View File

@ -55,9 +55,7 @@ Item {
rightIcon.text : Style.fa.chevron_circle_right rightIcon.text : Style.fa.chevron_circle_right
rightIcon.font.pointSize : Style.settings.toggleSize * Style.pt rightIcon.font.pointSize : Style.settings.toggleSize * Style.pt
onClicked: { onClicked: {
dialogGlobal.state="checkUpdates" go.checkForUpdates()
dialogGlobal.show()
dialogGlobal.confirmed()
} }
} }
@ -130,7 +128,7 @@ Item {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked : { onClicked : {
Qt.openUrlExternally(go.releaseNotesLink) Qt.openUrlExternally(go.updateReleaseNotesLink)
} }
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
} }

View File

@ -55,7 +55,7 @@ Window {
minimumWidth : Style.main.width minimumWidth : Style.main.width
minimumHeight : Style.main.height minimumHeight : Style.main.height
property bool isOutdateVersion : root.updateState == "forceUpgrade" property bool isOutdateVersion : root.updateState == "forceUpdate"
property bool activeContent : property bool activeContent :
!dialogAddUser .visible && !dialogAddUser .visible &&
@ -252,40 +252,7 @@ Window {
DialogUpdate { DialogUpdate {
id: dialogUpdate id: dialogUpdate
forceUpdate: root.isOutdateVersion
title: root.isOutdateVersion ?
qsTr("%1 is outdated", "title of outdate dialog").arg(go.programTitle):
qsTr("%1 update to %2", "title of update dialog").arg(go.programTitle).arg(go.newversion)
introductionText: {
if (root.isOutdateVersion) {
if (go.goos=="linux") {
return qsTr('You are using an outdated version of our software.<br>
Please dowload and install the latest version to continue using %1.<br><br>
<a href="%2">%2</a>',
"Message for force-update in Linux").arg(go.programTitle).arg(go.landingPage)
} else {
return qsTr('You are using an outdated version of our software.<br>
Please dowload and install the latest version to continue using %1.<br><br>
You can continue with update or download and install the new version manually from<br><br>
<a href="%2">%2</a>',
"Message for force-update in Win/Mac").arg(go.programTitle).arg(go.landingPage)
}
} else {
if (go.goos=="linux") {
return qsTr('A new version of %1 is available.<br>
Check <a href="%2">release notes</a> to learn what is new in %3.<br>
Use your package manager to update or download and install new the version manually from<br><br>
<a href="%4">%4</a>',
"Message for update in Linux").arg(go.programTitle).arg(go.releaseNotesLink).arg(go.newversion).arg(go.landingPage)
} else {
return qsTr('A new version of %1 is available.<br>
Check <a href="%2">release notes</a> to learn what is new in %3.<br>
You can continue with update or download and install new the version manually from<br><br>
<a href="%4">%4</a>',
"Message for update in Win/Mac").arg(go.programTitle).arg(go.releaseNotesLink).arg(go.newversion).arg(go.landingPage)
}
}
}
} }

View File

@ -75,6 +75,25 @@ Item {
onClicked: bugreportWin.show() onClicked: bugreportWin.show()
} }
ButtonIconText {
id: autoUpdates
text: qsTr("Keep the application up to date", "label for toggle that activates and disables the automatic updates")
leftIcon.text : Style.fa.download
rightIcon {
font.pointSize : Style.settings.toggleSize * Style.pt
text : go.isAutoUpdate!=false ? Style.fa.toggle_on : Style.fa.toggle_off
color : go.isAutoUpdate!=false ? Style.main.textBlue : Style.main.textDisabled
}
Accessible.description: (
go.isAutoUpdate == false ?
qsTr("Enable" , "Click to enable the automatic update of Bridge") :
qsTr("Disable" , "Click to disable the automatic update of Bridge")
) + " " + text
onClicked: {
go.toggleAutoUpdate()
}
}
/* /*
ButtonIconText { ButtonIconText {

View File

@ -25,16 +25,17 @@ import ProtonUI 1.0
Dialog { Dialog {
id: root id: root
title: "Bridge update "+go.newversion
property alias introductionText : introduction.text
property bool hasError : false property bool hasError : false
property bool forceUpdate : false
signal cancel() signal cancel()
signal okay() signal okay()
title: forceUpdate ?
qsTr("Update %1 now", "title of force update dialog").arg(go.programTitle):
qsTr("Update to %1 %2", "title of normal update dialog").arg(go.programTitle).arg(go.updateVersion)
isDialogBusy: currentIndex==1 isDialogBusy: currentIndex==1 || forceUpdate
Rectangle { // 0: Release notes and confirm Rectangle { // 0: Release notes and confirm
width: parent.width width: parent.width
@ -51,18 +52,45 @@ Dialog {
color: Style.dialog.text color: Style.dialog.text
linkColor: Style.dialog.textBlue linkColor: Style.dialog.textBlue
font { font {
pointSize: 0.8 * Style.dialog.fontSize * Style.pt pointSize: Style.dialog.fontSize * Style.pt
} }
width: 2*root.width/3 width: 2*root.width/3
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap wrapMode: Text.Wrap
// customize message per application text: {
text: ' <a href="%1">Release notes</a><br> New version %2<br> <br><br> <a href="%3">%3</a>' if (forceUpdate) {
if (go.updateCanInstall) {
return qsTr('You need to update this app to continue using it.<br>
Update now or manually download the most recent version here:<br>
<a href="%1">%1</a><br>
<a href="https://protonmail.com/support/knowledge-base/update-required/">Learn why</a> you need to update',
"Message for force-update").arg(go.updateLandingPage)
} else {
return qsTr('You need to update this app to continue using it.<br>
Download the most recent version here:<br>
<a href="%1">%1</a><br>
<a href="https://protonmail.com/support/knowledge-base/update-required/">Learn why</a> you need to update',
"Message for force-update").arg(go.updateLandingPage)
}
}
if (go.updateCanInstall) {
return qsTr('Update to the newest version or download it from:<br>
<a href="%1">%1</a><br>
<a href="%2">View release notes</a>',
"Message for manual update").arg(go.updateLandingPage).arg(go.updateReleaseNotesLink)
} else {
return qsTr('Update to the newest version from:<br>
<a href="%1">%1</a><br>
<a href="%2">View release notes</a>',
"Message for manual update").arg(go.updateLandingPage).arg(go.updateReleaseNotesLink)
}
}
onLinkActivated : { onLinkActivated : {
console.log("clicked link:", link) console.log("clicked link:", link)
root.hide()
Qt.openUrlExternally(link) Qt.openUrlExternally(link)
} }
@ -73,21 +101,30 @@ Dialog {
} }
} }
CheckBoxLabel {
id: autoUpdate
anchors.horizontalCenter: parent.horizontalCenter
text: qsTr("Automatically update in the future", "Checkbox label for using autoupdates later on")
checked: go.isAutoUpdate
onToggled: go.toggleAutoUpdate()
visible: !root.forceUpdate
}
Row { Row {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
spacing: Style.dialog.spacing spacing: Style.dialog.spacing
ButtonRounded { ButtonRounded {
fa_icon: Style.fa.times fa_icon: Style.fa.times
text: (go.goos=="linux" ? qsTr("Okay") : qsTr("Cancel")) text: root.forceUpdate ? qsTr("Quit") : qsTr("Cancel")
color_main: Style.dialog.text color_main: Style.dialog.text
onClicked: root.cancel() onClicked: root.forceUpdate ? Qt.quit() : root.cancel()
} }
ButtonRounded { ButtonRounded {
fa_icon: Style.fa.check fa_icon: Style.fa.check
text: qsTr("Update") text: qsTr("Update")
visible: go.goos!="linux" visible: go.updateCanInstall
color_main: Style.dialog.text color_main: Style.dialog.text
color_minor: Style.main.textBlue color_minor: Style.main.textBlue
isOpaque: true isOpaque: true
@ -97,7 +134,7 @@ Dialog {
} }
} }
Rectangle { // 0: Check / download / unpack / prepare Rectangle { // 1: Installing update
id: updateStatus id: updateStatus
width: parent.width width: parent.width
height: parent.height height: parent.height
@ -116,35 +153,29 @@ Dialog {
width: 2*root.width/3 width: 2*root.width/3
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap wrapMode: Text.Wrap
text: { text: qsTr("Updating...")
switch (go.progressDescription) {
case "1": return qsTr("Checking the current version.")
case "2": return qsTr("Downloading the update files.")
case "3": return qsTr("Verifying the update files.")
case "4": return qsTr("Unpacking the update files.")
case "5": return qsTr("Starting the update.")
case "6": return qsTr("Quitting the application.")
default: return ""
}
}
} }
ProgressBar { ProgressBar {
id: progressbar id: updateProgressBar
implicitWidth : 2*updateStatus.width/3 width: 2*updateStatus.width/3
implicitHeight : Style.exporting.rowHeight height: Style.exporting.rowHeight
visible: go.progress!=0 // hack hide animation when clearing out progress bar //implicitWidth : 2*updateStatus.width/3
value: go.progress //implicitHeight : Style.exporting.rowHeight
property int current: go.total * go.progress indeterminate: true
property bool isFinished: finishedPartBar.width == progressbar.width //value: 0.5
//property int current: go.total * go.progress
//property bool isFinished: finishedPartBar.width == progressbar.width
background: Rectangle { background: Rectangle {
radius : Style.exporting.boxRadius radius : Style.exporting.boxRadius
color : Style.exporting.progressBackground color : Style.exporting.progressBackground
} }
contentItem: Item { contentItem: Item {
clip: true
Rectangle { Rectangle {
id: finishedPartBar id: progressIndicator
width : parent.width * progressbar.visualPosition width : updateProgressBar.indeterminate ? 50 : parent.width * updateProgressBar.visualPosition
height : parent.height height : parent.height
radius : Style.exporting.boxRadius radius : Style.exporting.boxRadius
gradient : Gradient { gradient : Gradient {
@ -156,6 +187,27 @@ Dialog {
Behavior on width { Behavior on width {
NumberAnimation { duration:300; easing.type: Easing.InOutQuad } NumberAnimation { duration:300; easing.type: Easing.InOutQuad }
} }
SequentialAnimation {
running: updateProgressBar.visible && updateProgressBar.indeterminate
loops: Animation.Infinite
SmoothedAnimation {
target: progressIndicator
property: "x"
from: 0
to: updateProgressBar.width - progressIndicator.width
duration: 2000
}
SmoothedAnimation {
target: progressIndicator
property: "x"
from: updateProgressBar.width - progressIndicator.width
to: 0
duration: 2000
}
}
} }
Text { Text {
anchors.centerIn: parent anchors.centerIn: parent
@ -170,7 +222,7 @@ Dialog {
} }
} }
Rectangle { // 1: Something went wrong / All ok, closing bridge Rectangle { // 2: Something went wrong / All ok, closing bridge
width: parent.width width: parent.width
height: parent.height height: parent.height
color: Style.transparent color: Style.transparent
@ -188,8 +240,8 @@ Dialog {
width: 2*root.width/3 width: 2*root.width/3
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap wrapMode: Text.Wrap
text: !root.hasError ? qsTr('Application will quit now to finish the update.', "message after successful update") : text: !root.hasError ? qsTr('%1 will restart now to finish the update.', "message after successful update").arg(go.programTitle) :
qsTr('<b>The update procedure was not successful!</b><br>Please follow the download link and update manually. <br><br><a href="%1">%1</a>').arg(go.downloadLink) qsTr('<b>The update procedure was not successful!</b><br>Please follow the download link and update manually. <br><br><a href="%1">%1</a>').arg(go.updateLandingPage)
onLinkActivated : { onLinkActivated : {
console.log("clicked link:", link) console.log("clicked link:", link)
@ -220,7 +272,7 @@ Dialog {
function finished(hasError) { function finished(hasError) {
root.hasError = hasError root.hasError = hasError
root.incrementCurrentIndex() root.currentIndex = 2
} }
onShow: { onShow: {
@ -234,9 +286,10 @@ Dialog {
onOkay: { onOkay: {
switch (root.currentIndex) { switch (root.currentIndex) {
case 0: case 0:
go.startUpdate() go.startManualUpdate()
root.currentIndex = 1
break
} }
root.incrementCurrentIndex()
} }
onCancel: { onCancel: {

View File

@ -53,6 +53,7 @@ Rectangle {
} }
Row { Row {
id: messageRow
anchors.centerIn: root anchors.centerIn: root
visible: root.isVisible visible: root.isVisible
spacing: Style.main.leftMarginButton spacing: Style.main.leftMarginButton
@ -63,80 +64,74 @@ Rectangle {
} }
ClickIconText { ClickIconText {
id: linkText
anchors.verticalCenter : message.verticalCenter anchors.verticalCenter : message.verticalCenter
text : "("+go.newversion+" " + qsTr("release notes", "display the release notes from the new version")+")"
visible : root.state=="oldVersion"
iconText : "" iconText : ""
onClicked : {
Qt.openUrlExternally(go.releaseNotesLink)
}
fontSize : root.fontSize fontSize : root.fontSize
} }
ClickIconText { ClickIconText {
id: actionText
anchors.verticalCenter : message.verticalCenter anchors.verticalCenter : message.verticalCenter
text : root.state=="oldVersion" || root.state == "forceUpdate" ?
qsTr("Update", "click to update to a new version when one is available") :
qsTr("Retry now", "click to try to connect to the internet when the app is disconnected from the internet")
visible : root.state!="internetCheck"
iconText : "" iconText : ""
onClicked : {
if (root.state=="oldVersion" || root.state=="forceUpdate" ) {
winMain.dialogUpdate.show()
} else {
go.checkInternet()
}
}
fontSize : root.fontSize fontSize : root.fontSize
textUnderline: true textUnderline: true
} }
Text { Text {
id: separatorText
anchors.baseline : message.baseline anchors.baseline : message.baseline
color: Style.main.text color: Style.main.text
font { font {
pointSize : root.fontSize * Style.pt pointSize : root.fontSize * Style.pt
bold : true bold : true
} }
visible: root.state=="oldVersion" || root.state=="noInternet"
text : "|"
} }
ClickIconText { ClickIconText {
id: action2Text
anchors.verticalCenter : message.verticalCenter anchors.verticalCenter : message.verticalCenter
iconText : "" iconText : ""
text : root.state == "noInternet" ?
qsTr("Troubleshoot", "Show modal screen with additional tips for troubleshooting connection issues") :
qsTr("Remind me later", "Do not install new version and dismiss a notification")
visible : root.state=="oldVersion" || root.state=="noInternet"
onClicked : {
if (root.state == "oldVersion") {
root.state = "upToDate"
}
if (root.state == "noInternet") {
dialogConnectionTroubleshoot.show()
}
}
fontSize : root.fontSize fontSize : root.fontSize
textUnderline: true textUnderline: true
} }
} }
ClickIconText {
id: closeSign
anchors.verticalCenter : messageRow.verticalCenter
anchors.right: root.right
iconText : Style.fa.close
fontSize : root.fontSize
textUnderline: true
}
onStateChanged : { onStateChanged : {
switch (root.state) { switch (root.state) {
case "forceUpdate" : case "internetCheck":
gui.warningFlags |= Style.errorInfoBar break;
break;
case "upToDate" :
gui.warningFlags &= ~Style.warnInfoBar
iTry = 0
secLeft=checkInterval[iTry]
break;
case "noInternet" : case "noInternet" :
gui.warningFlags |= Style.warnInfoBar gui.warningFlags |= Style.warnInfoBar
retryInternet.start() retryInternet.start()
secLeft=checkInterval[iTry] secLeft=checkInterval[iTry]
break; break;
case "oldVersion":
gui.warningFlags |= Style.warnInfoBar
break;
case "forceUpdate":
gui.warningFlags |= Style.errorInfoBar
break;
case "upToDate":
gui.warningFlags &= ~Style.warnInfoBar
iTry = 0
secLeft=checkInterval[iTry]
break;
case "updateRestart":
gui.warningFlags |= Style.warnInfoBar
break;
case "updateError":
gui.warningFlags |= Style.errorInfoBar
break;
default : default :
gui.warningFlags |= Style.warnInfoBar break;
} }
if (root.state!="noInternet") { if (root.state!="noInternet") {
@ -172,6 +167,26 @@ Rectangle {
color: Style.main.background color: Style.main.background
text: qsTr("Checking connection. Please wait...", "displayed after user retries internet connection") text: qsTr("Checking connection. Please wait...", "displayed after user retries internet connection")
} }
PropertyChanges {
target: linkText
visible: false
}
PropertyChanges {
target: actionText
visible: false
}
PropertyChanges {
target: separatorText
visible: false
}
PropertyChanges {
target: action2Text
visible: false
}
PropertyChanges {
target: closeSign
visible: false
}
}, },
State { State {
name: "noInternet" name: "noInternet"
@ -186,6 +201,35 @@ Rectangle {
color: Style.main.line color: Style.main.line
text: qsTr("Cannot contact server. Retrying in ", "displayed when the app is disconnected from the internet or server has problems")+timeToRetry()+"." text: qsTr("Cannot contact server. Retrying in ", "displayed when the app is disconnected from the internet or server has problems")+timeToRetry()+"."
} }
PropertyChanges {
target: linkText
visible: false
}
PropertyChanges {
target: actionText
visible: true
text: qsTr("Retry now", "click to try to connect to the internet when the app is disconnected from the internet")
onClicked: {
go.checkInternet()
}
}
PropertyChanges {
target: separatorText
visible: true
text: "|"
}
PropertyChanges {
target: action2Text
visible: true
text: qsTr("Troubleshoot", "Show modal screen with additional tips for troubleshooting connection issues")
onClicked: {
dialogConnectionTroubleshoot.show()
}
}
PropertyChanges {
target: closeSign
visible: false
}
}, },
State { State {
name: "oldVersion" name: "oldVersion"
@ -198,7 +242,38 @@ Rectangle {
PropertyChanges { PropertyChanges {
target: message target: message
color: Style.main.background color: Style.main.background
text: qsTr("An update is available.", "displayed in a notification when an app update is available") text: qsTr("Update available", "displayed in a notification when an app update is available")
}
PropertyChanges {
target: linkText
visible: true
text: "(" + qsTr("view release notes", "display the release notes from the new version") + ")"
onClicked: {
Qt.openUrlExternally(go.updateReleaseNotesLink)
}
}
PropertyChanges {
target: actionText
visible: true
text: qsTr("Update", "click to update to a new version when one is available")
onClicked: {
winMain.dialogUpdate.show()
}
}
PropertyChanges {
target: separatorText
visible: false
}
PropertyChanges {
target: action2Text
visible: false
}
PropertyChanges {
target: closeSign
visible: true
onClicked: {
root.state = "upToDate"
}
} }
}, },
State { State {
@ -214,6 +289,30 @@ Rectangle {
color: Style.main.line color: Style.main.line
text: qsTr("%1 is outdated.", "displayed in a notification when app is outdated").arg(go.programTitle) text: qsTr("%1 is outdated.", "displayed in a notification when app is outdated").arg(go.programTitle)
} }
PropertyChanges {
target: linkText
visible: false
}
PropertyChanges {
target: actionText
visible: true
text: qsTr("Update", "click to update to a new version when one is available")
onClicked: {
winMain.dialogUpdate.show()
}
}
PropertyChanges {
target: separatorText
visible: false
}
PropertyChanges {
target: action2Text
visible: false
}
PropertyChanges {
target: closeSign
visible: false
}
}, },
State { State {
name: "upToDate" name: "upToDate"
@ -228,6 +327,103 @@ Rectangle {
color: Style.main.background color: Style.main.background
text: "" text: ""
} }
PropertyChanges {
target: linkText
visible: false
}
PropertyChanges {
target: actionText
visible: false
}
PropertyChanges {
target: separatorText
visible: false
}
PropertyChanges {
target: action2Text
visible: false
}
PropertyChanges {
target: closeSign
visible: false
}
},
State {
name: "updateRestart"
PropertyChanges {
target: root
height: 2* Style.main.fontSize
isVisible: true
color: Style.main.textBlue
}
PropertyChanges {
target: message
color: Style.main.background
text: qsTr("%1 update is ready", "displayed in a notification when an app update is installed and restart is needed").arg(go.programTitle)
}
PropertyChanges {
target: linkText
visible: false
}
PropertyChanges {
target: actionText
visible: true
text: qsTr("Restart now", "click to restart application as new version was installed")
onClicked: {
go.setToRestart()
Qt.quit()
}
}
PropertyChanges {
target: separatorText
visible: false
}
PropertyChanges {
target: action2Text
visible: false
}
PropertyChanges {
target: closeSign
visible: false
}
},
State {
name: "updateError"
PropertyChanges {
target: root
height: 2* Style.main.fontSize
isVisible: true
color: Style.main.textRed
}
PropertyChanges {
target: message
color: Style.main.line
text: qsTr("Sorry, %1 couldn't update.", "displayed in a notification when app failed to autoupdate").arg(go.programTitle)
}
PropertyChanges {
target: linkText
visible: false
}
PropertyChanges {
target: actionText
visible: true
text: qsTr("Please update manually", "click to open download page to update manally")
onClicked: {
Qt.openUrlExternally(go.updateLandingPage)
}
}
PropertyChanges {
target: separatorText
visible: false
}
PropertyChanges {
target: action2Text
visible: false
}
PropertyChanges {
target: closeSign
visible: false
}
} }
] ]
} }

View File

@ -59,7 +59,6 @@ QtObject {
property real fontSize : 12 * px property real fontSize : 12 * px
property real iconSize : 15 * px property real iconSize : 15 * px
property real leftMarginButton : 9 * px property real leftMarginButton : 9 * px
property real verCheckRepeatTime : 15*60*60*1000 // milliseconds
property real topMargin : fontSize property real topMargin : fontSize
property real bottomMargin : fontSize property real bottomMargin : fontSize
property real border : 1 * px property real border : 1 * px

View File

@ -108,9 +108,14 @@ Window {
ListElement { title: "Logout bridge" } ListElement { title: "Logout bridge" }
ListElement { title: "Internet on" } ListElement { title: "Internet on" }
ListElement { title: "Internet off" } ListElement { title: "Internet off" }
ListElement { title: "NeedUpdate" }
ListElement { title: "UpToDate" } ListElement { title: "UpToDate" }
ListElement { title: "ForceUpdate" } ListElement { title: "NotifyManualUpdate(CanInstall)" }
ListElement { title: "NotifyManualUpdate(CantInstall)" }
ListElement { title: "NotifyManualUpdateRestart" }
ListElement { title: "NotifyManualUpdateError" }
ListElement { title: "ForceUpdate" }
ListElement { title: "NotifySilentUpdateRestartNeeded" }
ListElement { title: "NotifySilentUpdateError" }
ListElement { title: "Linux" } ListElement { title: "Linux" }
ListElement { title: "Windows" } ListElement { title: "Windows" }
ListElement { title: "Macos" } ListElement { title: "Macos" }
@ -196,12 +201,29 @@ Window {
case "UpToDate" : case "UpToDate" :
testroot.newVersion = false testroot.newVersion = false
break; break;
case "NeedUpdate" : case "NotifyManualUpdate(CanInstall)" :
testroot.newVersion = true go.notifyManualUpdate()
break; go.updateCanInstall = true
break;
case "NotifyManualUpdate(CantInstall)" :
go.notifyManualUpdate()
go.updateCanInstall = false
break;
case "NotifyManualUpdateRestart":
go.notifyManualUpdateRestartNeeded()
break;
case "NotifyManualUpdateError":
go.notifyManualUpdateError()
break;
case "ForceUpdate" : case "ForceUpdate" :
go.notifyUpdate() go.notifyForceUpdate()
break; break;
case "NotifySilentUpdateRestartNeeded" :
go.notifySilentUpdateRestartNeeded()
break;
case "NotifySilentUpdateError" :
go.notifySilentUpdateError()
break;
case "SendAlertPopup" : case "SendAlertPopup" :
go.showOutgoingNoEncPopup("Alert sending unencrypted!") go.showOutgoingNoEncPopup("Alert sending unencrypted!")
break; break;
@ -244,6 +266,7 @@ Window {
id: go id: go
property bool isAutoStart : true property bool isAutoStart : true
property bool isAutoUpdate : false
property bool isProxyAllowed : false property bool isProxyAllowed : false
property bool isFirstStart : false property bool isFirstStart : false
property bool isFreshVersion : false property bool isFreshVersion : false
@ -269,17 +292,35 @@ Window {
property string genericErrSeeLogs property string genericErrSeeLogs
property string programTitle : "ProtonMail Bridge" property string programTitle : "ProtonMail Bridge"
property string newversion : "QA.1.0"
property string fullversion : "QA.1.0 (d9f8sdf9) 2020-02-19T10:57:23+01:00" property string fullversion : "QA.1.0 (d9f8sdf9) 2020-02-19T10:57:23+01:00"
property string landingPage : "https://landing.page"
//property string downloadLink: "https://landing.page/download/link"
property string downloadLink: "https://protonmail.com/download/beta/protonmail-bridge-1.1.5-1.x86_64.rpm;https://www.protonmail.com/downloads/beta/Desktop-Bridge-link1.exe;https://www.protonmail.com/downloads/beta/Desktop-Bridge-link1.exe;https://www.protonmail.com/downloads/beta/Desktop-Bridge-link1.exe;" property string downloadLink: "https://protonmail.com/download/beta/protonmail-bridge-1.1.5-1.x86_64.rpm;https://www.protonmail.com/downloads/beta/Desktop-Bridge-link1.exe;https://www.protonmail.com/downloads/beta/Desktop-Bridge-link1.exe;https://www.protonmail.com/downloads/beta/Desktop-Bridge-link1.exe;"
property string releaseNotesLink : "https://protonmail.com/download/bridge/release_notes.html"
property string updateVersion : "QA.1.0"
property bool updateCanInstall: true
property string updateLandingPage : "https://protonmail.com/bridge/download/"
property string updateReleaseNotesLink : "https://protonmail.com/download/bridge/release_notes.html"
signal notifyManualUpdate()
signal notifyManualUpdateRestartNeeded()
signal notifyManualUpdateError()
signal notifyForceUpdate()
signal notifySilentUpdateRestartNeeded()
signal notifySilentUpdateError()
function checkForUpdates() {
console.log("checkForUpdates")
}
function startManualUpdate() {
console.log("startManualUpdate")
}
property string credits : "here;goes;list;;of;;used;packages;" property string credits : "here;goes;list;;of;;used;packages;"
property real progress: 0.3 property real progress: 0.3
property int progressDescription: 2 property int progressDescription: 2
function setToRestart() {
console.log("setting to restart")
}
signal toggleMainWin(int systX, int systY, int systW, int systH) signal toggleMainWin(int systX, int systY, int systW, int systH)
@ -295,12 +336,11 @@ Window {
signal processFinished() signal processFinished()
signal toggleAutoStart() signal toggleAutoStart()
signal toggleAutoUpdate()
signal notifyBubble(int tabIndex, string message) signal notifyBubble(int tabIndex, string message)
signal silentBubble(int tabIndex, string message) signal silentBubble(int tabIndex, string message)
signal runCheckVersion(bool showMessage)
signal setAddAccountWarning(string message) signal setAddAccountWarning(string message)
signal notifyUpdate()
signal notifyFirewall() signal notifyFirewall()
signal notifyLogout(string accname) signal notifyLogout(string accname)
signal notifyAddressChanged(string accname) signal notifyAddressChanged(string accname)
@ -463,9 +503,6 @@ Window {
switch (timer.work) { switch (timer.work) {
case "wait": case "wait":
break break
case "startUpdate":
go.animateProgressBar.start()
go.updateFinished(true)
default: default:
go.processFinished() go.processFinished()
} }
@ -476,11 +513,6 @@ Window {
timer.start() timer.start()
} }
function startUpdate() {
timer.work="startUpdate"
timer.start()
}
function loadAccounts() { function loadAccounts() {
console.log("Test: Account loaded") console.log("Test: Account loaded")
} }
@ -500,21 +532,7 @@ Window {
} }
function getLocalVersionInfo(){ function getLocalVersionInfo(){
go.newversion = "QA.1.0" go.updateVersion = "QA.1.0"
}
function isNewVersionAvailable(showMessage){
if (testroot.newVersion) {
go.newversion = "QA.2.0"
setUpdateState("oldVersion")
} else {
go.newversion = "QA.1.0"
setUpdateState("upToDate")
if(showMessage) {
notifyVersionIsTheLatest()
}
}
workAndClose()
} }
function getBackendVersion() { function getBackendVersion() {
@ -603,6 +621,12 @@ Window {
isAutoStart = (isAutoStart!=false) ? false : true isAutoStart = (isAutoStart!=false) ? false : true
console.log (" Test: toggleAutoStart "+isAutoStart) console.log (" Test: toggleAutoStart "+isAutoStart)
} }
onToggleAutoUpdate: {
workAndClose()
isAutoUpdate = (isAutoUpdate!=false) ? false : true
console.log (" Test: onToggleAutoUpdate "+isAutoUpdate)
}
} }
} }

View File

@ -98,24 +98,29 @@ Window {
ListModel { ListModel {
id: buttons id: buttons
ListElement { title : "Show window" } ListElement { title : "Show window" }
ListElement { title : "Logout" } ListElement { title : "Logout" }
ListElement { title : "Internet on" } ListElement { title : "Internet on" }
ListElement { title : "Internet off" } ListElement { title : "Internet off" }
ListElement { title : "Macos" } ListElement { title : "Macos" }
ListElement { title : "Windows" } ListElement { title : "Windows" }
ListElement { title : "Linux" } ListElement { title : "Linux" }
ListElement { title : "New Version" } ListElement { title: "NotifyManualUpdate(CanInstall)" }
ListElement { title : "ForceUpgrade" } ListElement { title: "NotifyManualUpdate(CantInstall)" }
ListElement { title : "ImportStructure" } ListElement { title: "NotifyManualUpdateRestart" }
ListElement { title : "DraftImpFailed" } ListElement { title: "NotifyManualUpdateError" }
ListElement { title : "NoInterImp" } ListElement { title: "ForceUpdate" }
ListElement { title : "ReportImp" } ListElement { title: "NotifySilentUpdateRestartNeeded" }
ListElement { title : "NewFolder" } ListElement { title: "NotifySilentUpdateError" }
ListElement { title : "EditFolder" } ListElement { title : "ImportStructure" }
ListElement { title : "EditLabel" } ListElement { title : "DraftImpFailed" }
ListElement { title : "ExpProgErr" } ListElement { title : "NoInterImp" }
ListElement { title : "ImpProgErr" } ListElement { title : "ReportImp" }
ListElement { title : "NewFolder" }
ListElement { title : "EditFolder" }
ListElement { title : "EditLabel" }
ListElement { title : "ExpProgErr" }
ListElement { title : "ImpProgErr" }
} }
ListView { ListView {
@ -161,13 +166,29 @@ Window {
case "Linux" : case "Linux" :
go.goos = "linux"; go.goos = "linux";
break; break;
case "New Version" : case "NotifyManualUpdate(CanInstall)" :
testroot.newVersion = !testroot.newVersion go.notifyManualUpdate()
systrText.text = testroot.newVersion ? "new version" : "uptodate" go.updateCanInstall = true
break break;
case "ForceUpgrade" : case "NotifyManualUpdate(CantInstall)" :
go.notifyUpgrade() go.notifyManualUpdate()
break; go.updateCanInstall = false
break;
case "NotifyManualUpdateRestart":
go.notifyManualUpdateRestartNeeded()
break;
case "NotifyManualUpdateError":
go.notifyManualUpdateError()
break;
case "ForceUpdate" :
go.notifyForceUpdate()
break;
case "NotifySilentUpdateRestartNeeded" :
go.notifySilentUpdateRestartNeeded()
break;
case "NotifySilentUpdateError" :
go.notifySilentUpdateError()
break;
case "ImportStructure" : case "ImportStructure" :
testgui.winMain.dialogImport.address = "cuto@pm.com" testgui.winMain.dialogImport.address = "cuto@pm.com"
testgui.winMain.dialogImport.show() testgui.winMain.dialogImport.show()
@ -815,6 +836,7 @@ Window {
id: go id: go
property int isAutoStart : 1 property int isAutoStart : 1
property bool isAutoUpdate : false
property bool isFirstStart : false property bool isFirstStart : false
property string currentAddress : "none" property string currentAddress : "none"
//property string goos : "windows" //property string goos : "windows"
@ -831,9 +853,25 @@ Window {
property string bugReportSent property string bugReportSent
property string programTitle : "ProtonMail Import-Export app" property string programTitle : "ProtonMail Import-Export app"
property string newversion : "q0.1.0" property string fullversion : "QA.1.0 (d9f8sdf9) 2020-02-19T10:57:23+01:00"
property string landingPage : "https://landing.page" property string downloadLink: "https://protonmail.com/download/beta/protonmail-bridge-1.1.5-1.x86_64.rpm;https://www.protonmail.com/downloads/beta/Desktop-Bridge-link1.exe;https://www.protonmail.com/downloads/beta/Desktop-Bridge-link1.exe;https://www.protonmail.com/downloads/beta/Desktop-Bridge-link1.exe;"
property string releaseNotesLink : "https://protonmail.com/download/ie/release_notes.html"
property string updateVersion : "q0.1.0"
property bool updateCanInstall: true
property string updateLandingPage : "https://protonmail.com/import-export/download/"
property string updateReleaseNotesLink : "https://protonmail.com/download/ie/release_notes.html"
signal notifyManualUpdate()
signal notifyManualUpdateRestartNeeded()
signal notifyManualUpdateError()
signal notifyForceUpdate()
signal notifySilentUpdateRestartNeeded()
signal notifySilentUpdateError()
function checkForUpdates() {
console.log("checkForUpdates")
}
function startManualUpdate() {
console.log("startManualUpdate")
}
property real progress: 0.0 property real progress: 0.0
property int progressFails: 0 property int progressFails: 0
@ -846,13 +884,10 @@ Window {
signal toggleMainWin(int systX, int systY, int systW, int systH) signal toggleMainWin(int systX, int systY, int systW, int systH)
signal notifyHasNoKeychain() signal notifyHasNoKeychain()
signal notifyKeychainRebuild() signal notifyKeychainRebuild()
signal notifyAddressChangedLogout() signal notifyAddressChangedLogout()
signal notifyAddressChanged() signal notifyAddressChanged()
signal notifyUpdate()
signal showWindow() signal showWindow()
signal showHelp() signal showHelp()
@ -871,10 +906,10 @@ Window {
signal processFinished() signal processFinished()
signal toggleAutoStart() signal toggleAutoStart()
signal toggleAutoUpdate()
signal notifyBubble(int tabIndex, string message) signal notifyBubble(int tabIndex, string message)
signal runCheckVersion(bool showMessage)
signal setAddAccountWarning(string message) signal setAddAccountWarning(string message)
signal notifyUpgrade() signal notifyUpdate()
signal updateFinished(bool hasError) signal updateFinished(bool hasError)
signal notifyLogout(string accname) signal notifyLogout(string accname)
@ -882,6 +917,10 @@ Window {
signal notifyError(int errCode) signal notifyError(int errCode)
property string errorDescription : "" property string errorDescription : ""
function setToRestart() {
console.log("setting to restart")
}
function delay(duration) { function delay(duration) {
var timeStart = new Date().getTime(); var timeStart = new Date().getTime();
@ -955,7 +994,7 @@ Window {
workAndClose("addAccount") workAndClose("addAccount")
} }
property SequentialAnimation animateProgressBarUpgrade : SequentialAnimation { property SequentialAnimation animateProgressBarUpdate : SequentialAnimation {
// version // version
PropertyAnimation{ target: go; properties: "progressDescription"; to: 1; duration: 1; } PropertyAnimation{ target: go; properties: "progressDescription"; to: 1; duration: 1; }
PropertyAnimation{ duration: 2000; } PropertyAnimation{ duration: 2000; }
@ -1066,7 +1105,6 @@ Window {
onTriggered : { onTriggered : {
console.log("triggered "+timer.work) console.log("triggered "+timer.work)
switch (timer.work) { switch (timer.work) {
case "isNewVersionAvailable" :
case "clearCache" : case "clearCache" :
case "clearKeychain" : case "clearKeychain" :
case "logout" : case "logout" :
@ -1093,8 +1131,8 @@ Window {
go.animateProgressBar.start() go.animateProgressBar.start()
break; break;
case "startUpgrade": case "startManualUpdate":
go.animateProgressBarUpgrade.start() go.animateProgressBarUpdate.start()
go.updateFinished(true) go.updateFinished(true)
default: default:
@ -1105,18 +1143,10 @@ Window {
function workAndClose(workDescription) { function workAndClose(workDescription) {
go.progress=0.0 go.progress=0.0
timer.work = workDescription timer.work = workDescription === undefined ? "" : workDescription
timer.start() timer.start()
} }
function startUpgrade() {
timer.work="startUpgrade"
timer.start()
}
function checkPathStatus(path) { function checkPathStatus(path) {
if ( path == "" ) return testgui.enums.pathEmptyPath if ( path == "" ) return testgui.enums.pathEmptyPath
if ( path == "wrong" ) return testgui.enums.pathWrongPath if ( path == "wrong" ) return testgui.enums.pathWrongPath
@ -1218,20 +1248,6 @@ Window {
workAndClose("switchAddressMode") workAndClose("switchAddressMode")
} }
function isNewVersionAvailable(showMessage){
if (testroot.newVersion) {
setUpdateState("oldVersion")
} else {
setUpdateState("upToDate")
if(showMessage) {
notifyVersionIsTheLatest()
}
}
workAndClose("isNewVersionAvailable")
//notifyBubble(2,go.versionCheckFailed)
return 0
}
function getLocalVersionInfo(){} function getLocalVersionInfo(){}
function getBackendVersion() { function getBackendVersion() {
@ -1328,5 +1344,11 @@ Window {
console.log("sending import report from ", address, " file ", fname) console.log("sending import report from ", address, " file ", fname)
return !fname.includes("fail") return !fname.includes("fail")
} }
onToggleAutoUpdate: {
workAndClose()
isAutoUpdate = (isAutoUpdate!=false) ? false : true
console.log (" Test: onToggleAutoUpdate "+isAutoUpdate)
}
} }
} }

View File

@ -23,6 +23,7 @@ import (
"errors" "errors"
"os" "os"
"github.com/ProtonMail/proton-bridge/internal/config/settings"
"github.com/ProtonMail/proton-bridge/internal/events" "github.com/ProtonMail/proton-bridge/internal/events"
qtcommon "github.com/ProtonMail/proton-bridge/internal/frontend/qt-common" qtcommon "github.com/ProtonMail/proton-bridge/internal/frontend/qt-common"
"github.com/ProtonMail/proton-bridge/internal/frontend/types" "github.com/ProtonMail/proton-bridge/internal/frontend/types"
@ -50,6 +51,7 @@ var log = logrus.WithField("pkg", "frontend-qt-ie")
type FrontendQt struct { type FrontendQt struct {
panicHandler types.PanicHandler panicHandler types.PanicHandler
locations *locations.Locations locations *locations.Locations
settings *settings.Settings
eventListener listener.Listener eventListener listener.Listener
updater types.Updater updater types.Updater
ie types.ImportExporter ie types.ImportExporter
@ -71,14 +73,17 @@ type FrontendQt struct {
progress *transfer.Progress progress *transfer.Progress
restarter types.Restarter restarter types.Restarter
// saving most up-to-date update info to install it manually
updateInfo updater.VersionInfo
} }
// New is constructor for Import-Export Qt-Go interface // New is constructor for Import-Export Qt-Go interface
func New( func New(
version, buildVersion string, version, buildVersion string,
panicHandler types.PanicHandler, panicHandler types.PanicHandler,
locations *locations.Locations, locations *locations.Locations,
settings *settings.Settings,
eventListener listener.Listener, eventListener listener.Listener,
updater types.Updater, updater types.Updater,
ie types.ImportExporter, ie types.ImportExporter,
@ -87,6 +92,7 @@ func New(
f := &FrontendQt{ f := &FrontendQt{
panicHandler: panicHandler, panicHandler: panicHandler,
locations: locations, locations: locations,
settings: settings,
programName: "ProtonMail Import-Export", programName: "ProtonMail Import-Export",
programVersion: "v" + version, programVersion: "v" + version,
eventListener: eventListener, eventListener: eventListener,
@ -111,9 +117,21 @@ func (f *FrontendQt) Loop() (err error) {
return err return err
} }
func (f *FrontendQt) NotifyManualUpdate(update updater.VersionInfo) error { func (f *FrontendQt) NotifyManualUpdate(update updater.VersionInfo, canInstall bool) {
// NOTE: Save the update somewhere so that it can be installed when user chooses "install now". f.Qml.SetUpdateVersion(update.Version.String())
return nil f.Qml.SetUpdateLandingPage(update.Landing)
f.Qml.SetUpdateReleaseNotesLink("https://protonmail.com/download/ie/release_notes.html")
f.Qml.SetUpdateCanInstall(canInstall)
f.updateInfo = update
f.Qml.NotifyManualUpdate()
}
func (f *FrontendQt) NotifySilentUpdateInstalled() {
f.Qml.NotifySilentUpdateRestartNeeded()
}
func (f *FrontendQt) NotifySilentUpdateError(err error) {
f.Qml.NotifySilentUpdateError()
} }
func (f *FrontendQt) watchEvents() { func (f *FrontendQt) watchEvents() {
@ -152,7 +170,7 @@ func (f *FrontendQt) watchEvents() {
f.Qml.NotifyLogout(user.Username()) f.Qml.NotifyLogout(user.Username())
case <-updateApplicationCh: case <-updateApplicationCh:
f.Qml.ProcessFinished() f.Qml.ProcessFinished()
f.Qml.NotifyUpdate() f.Qml.NotifyForceUpdate()
case <-newUserCh: case <-newUserCh:
f.Qml.LoadAccounts() f.Qml.LoadAccounts()
} }
@ -219,6 +237,12 @@ func (f *FrontendQt) QtExecute(Procedure func(*FrontendQt) error) error {
f.Qml.SetCredits(importexport.Credits) f.Qml.SetCredits(importexport.Credits)
f.Qml.SetFullversion(f.buildVersion) f.Qml.SetFullversion(f.buildVersion)
if f.settings.GetBool(settings.AutoUpdateKey) {
f.Qml.SetIsAutoUpdate(true)
} else {
f.Qml.SetIsAutoUpdate(false)
}
// Loop // Loop
if ret := gui.QGuiApplication_Exec(); ret != 0 { if ret := gui.QGuiApplication_Exec(); ret != 0 {
//err := errors.New(errors.ErrQApplication, "Event loop ended with return value: %v", string(ret)) //err := errors.New(errors.ErrQApplication, "Event loop ended with return value: %v", string(ret))
@ -299,6 +323,18 @@ func (f *FrontendQt) sendBug(description, emailClient, address string) bool {
return true return true
} }
func (f *FrontendQt) toggleAutoUpdate() {
defer f.Qml.ProcessFinished()
if f.settings.GetBool(settings.AutoUpdateKey) {
f.settings.SetBool(settings.AutoUpdateKey, false)
f.Qml.SetIsAutoUpdate(false)
} else {
f.settings.SetBool(settings.AutoUpdateKey, true)
f.Qml.SetIsAutoUpdate(true)
}
}
// checkInternet is almost idetical to bridge // checkInternet is almost idetical to bridge
func (f *FrontendQt) checkInternet() { func (f *FrontendQt) checkInternet() {
f.Qml.SetConnectionStatus(f.ie.CheckConnection() == nil) f.Qml.SetConnectionStatus(f.ie.CheckConnection() == nil)
@ -369,21 +405,43 @@ func (f *FrontendQt) setProgressManager(progress *transfer.Progress) {
}() }()
} }
func (f *FrontendQt) StartUpdate() { func (f *FrontendQt) startManualUpdate() {
// NOTE: Fix this. go func() {
err := f.updater.InstallUpdate(f.updateInfo)
if err != nil {
logrus.WithError(err).Error("An error occurred while installing updates manually")
f.Qml.NotifyManualUpdateError()
}
f.Qml.NotifyManualUpdateRestartNeeded()
}()
} }
// isNewVersionAvailable is identical to bridge func (f *FrontendQt) checkForUpdates() {
// return 0 when local version is fine
// return 1 when new version is available
func (f *FrontendQt) isNewVersionAvailable(showMessage bool) {
go func() { go func() {
defer f.Qml.ProcessFinished() version, err := f.updater.Check()
f.Qml.SetConnectionStatus(true) // if we are here connection is ok
f.Qml.SetUpdateState(StatusUpToDate) if err != nil {
if showMessage { logrus.WithError(err).Error("An error occurred while checking updates manually")
f.Qml.NotifyVersionIsTheLatest() f.Qml.NotifyManualUpdateError()
return
} }
if !f.updater.IsUpdateApplicable(version) {
logrus.Debug("No need to update")
return
}
logrus.WithField("version", version.Version).Info("An update is available")
if !f.updater.CanInstall(version) {
logrus.Debug("A manual update is required")
f.NotifyManualUpdate(version, false)
return
}
f.NotifyManualUpdate(version, true)
}() }()
} }

View File

@ -23,6 +23,7 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"github.com/ProtonMail/proton-bridge/internal/config/settings"
"github.com/ProtonMail/proton-bridge/internal/frontend/types" "github.com/ProtonMail/proton-bridge/internal/frontend/types"
"github.com/ProtonMail/proton-bridge/internal/locations" "github.com/ProtonMail/proton-bridge/internal/locations"
"github.com/ProtonMail/proton-bridge/internal/updater" "github.com/ProtonMail/proton-bridge/internal/updater"
@ -42,15 +43,21 @@ func (s *FrontendHeadless) Loop() error {
return http.ListenAndServe(":8082", nil) return http.ListenAndServe(":8082", nil)
} }
func (s *FrontendHeadless) NotifyManualUpdate(update updater.VersionInfo) error { func (s *FrontendHeadless) NotifyManualUpdate(update updater.VersionInfo, canInstall bool) {
// NOTE: Save the update somewhere so that it can be installed when user chooses "install now". // NOTE: Save the update somewhere so that it can be installed when user chooses "install now".
return nil }
func (s *FrontendHeadless) NotifySilentUpdateInstalled() {
}
func (s *FrontendHeadless) NotifySilentUpdateError(err error) {
} }
func New( func New(
version, buildVersion string, version, buildVersion string,
panicHandler types.PanicHandler, panicHandler types.PanicHandler,
locations *locations.Locations, locations *locations.Locations,
settings *settings.Settings,
eventListener listener.Listener, eventListener listener.Listener,
updater types.Updater, updater types.Updater,
ie types.ImportExporter, ie types.ImportExporter,

View File

@ -33,6 +33,7 @@ type GoQMLInterface struct {
_ func() `constructor:"init"` _ func() `constructor:"init"`
_ bool `property:"isAutoUpdate"`
_ string `property:"currentAddress"` _ string `property:"currentAddress"`
_ string `property:"goos"` _ string `property:"goos"`
_ string `property:"credits"` _ string `property:"credits"`
@ -49,11 +50,21 @@ type GoQMLInterface struct {
_ string `property:importLogFileName` _ string `property:importLogFileName`
_ string `property:"programTitle"` _ string `property:"programTitle"`
_ string `property:"newversion"`
_ string `property:"fullversion"` _ string `property:"fullversion"`
_ string `property:"downloadLink"` _ string `property:"downloadLink"`
_ string `property:"landingPage"`
_ string `property:"releaseNotesLink"` _ string `property:"updateVersion"`
_ bool `property:"updateCanInstall"`
_ string `property:"updateLandingPage"`
_ string `property:"updateReleaseNotesLink"`
_ func() `signal:"notifyManualUpdate"`
_ func() `signal:"notifyManualUpdateRestartNeeded"`
_ func() `signal:"notifyManualUpdateError"`
_ func() `signal:"notifyForceUpdate"`
_ func() `signal:"notifySilentUpdateRestartNeeded"`
_ func() `signal:"notifySilentUpdateError"`
_ func() `slot:"checkForUpdates"`
_ func() `slot:"startManualUpdate"`
// translations // translations
_ string `property:"wrongCredentials"` _ string `property:"wrongCredentials"`
@ -79,6 +90,7 @@ type GoQMLInterface struct {
_ func() `signal:"showWindow"` _ func() `signal:"showWindow"`
_ func() `slot:"toggleAutoUpdate"`
_ func() `slot:"quit"` _ func() `slot:"quit"`
_ func() `slot:"loadAccounts"` _ func() `slot:"loadAccounts"`
_ func() `slot:"openLogs"` _ func() `slot:"openLogs"`
@ -89,8 +101,7 @@ type GoQMLInterface struct {
_ func() `signal:"highlightSystray"` _ func() `signal:"highlightSystray"`
_ func() `signal:"normalSystray"` _ func() `signal:"normalSystray"`
_ func(showMessage bool) `slot:"isNewVersionAvailable"` _ func() string `slot:"getBackendVersion"`
_ func() string `slot:"getBackendVersion"`
_ func(description, client, address string) bool `slot:"sendBug"` _ func(description, client, address string) bool `slot:"sendBug"`
_ func(address string) bool `slot:"sendImportReport"` _ func(address string) bool `slot:"sendImportReport"`
@ -128,12 +139,10 @@ type GoQMLInterface struct {
_ func() `signal:"notifyVersionIsTheLatest"` _ func() `signal:"notifyVersionIsTheLatest"`
_ func() `signal:"notifyKeychainRebuild"` _ func() `signal:"notifyKeychainRebuild"`
_ func() `signal:"notifyHasNoKeychain"` _ func() `signal:"notifyHasNoKeychain"`
_ func() `signal:"notifyUpdate"`
_ func(accname string) `signal:"notifyLogout"` _ func(accname string) `signal:"notifyLogout"`
_ func(accname string) `signal:"notifyAddressChanged"` _ func(accname string) `signal:"notifyAddressChanged"`
_ func(accname string) `signal:"notifyAddressChangedLogout"` _ func(accname string) `signal:"notifyAddressChangedLogout"`
_ func() `slot:"startUpdate"`
_ func(hasError bool) `signal:"updateFinished"` _ func(hasError bool) `signal:"updateFinished"`
// errors // errors
@ -150,6 +159,7 @@ func (s *GoQMLInterface) init() {}
func (s *GoQMLInterface) SetFrontend(f *FrontendQt) { func (s *GoQMLInterface) SetFrontend(f *FrontendQt) {
s.ConnectQuit(f.App.Quit) s.ConnectQuit(f.App.Quit)
s.ConnectToggleAutoUpdate(f.toggleAutoUpdate)
s.ConnectLoadAccounts(f.Accounts.LoadAccounts) s.ConnectLoadAccounts(f.Accounts.LoadAccounts)
s.ConnectOpenLogs(f.openLogs) s.ConnectOpenLogs(f.openLogs)
s.ConnectOpenDownloadLink(f.openDownloadLink) s.ConnectOpenDownloadLink(f.openDownloadLink)
@ -170,10 +180,10 @@ func (s *GoQMLInterface) SetFrontend(f *FrontendQt) {
s.SetProgramTitle(f.programName) s.SetProgramTitle(f.programName)
s.ConnectOpenLicenseFile(f.openLicenseFile) s.ConnectOpenLicenseFile(f.openLicenseFile)
s.SetReleaseNotesLink("https://protonmail.com/download/ie/release_notes.html") s.SetUpdateReleaseNotesLink("https://protonmail.com/download/ie/release_notes.html")
s.ConnectGetLocalVersionInfo(f.getLocalVersionInfo) s.ConnectGetLocalVersionInfo(f.getLocalVersionInfo)
s.ConnectIsNewVersionAvailable(f.isNewVersionAvailable) s.ConnectCheckForUpdates(f.checkForUpdates)
s.ConnectGetBackendVersion(func() string { s.ConnectGetBackendVersion(func() string {
return f.programVersion return f.programVersion
}) })
@ -193,7 +203,5 @@ func (s *GoQMLInterface) SetFrontend(f *FrontendQt) {
s.ConnectCheckPathStatus(CheckPathStatus) s.ConnectCheckPathStatus(CheckPathStatus)
s.ConnectStartUpdate(f.StartUpdate)
s.ConnectEmitEvent(f.emitEvent) s.ConnectEmitEvent(f.emitEvent)
} }

View File

@ -95,6 +95,9 @@ type FrontendQt struct {
userIDAdded string userIDAdded string
restarter types.Restarter restarter types.Restarter
// saving most up-to-date update info to install it manually
updateInfo updater.VersionInfo
} }
// New returns a new Qt frontend for the bridge. // New returns a new Qt frontend for the bridge.
@ -173,9 +176,21 @@ func (s *FrontendQt) Loop() (err error) {
return err return err
} }
func (s *FrontendQt) NotifyManualUpdate(update updater.VersionInfo) error { func (s *FrontendQt) NotifyManualUpdate(update updater.VersionInfo, canInstall bool) {
// NOTE: Save the update somewhere so that it can be installed when user chooses "install now". s.Qml.SetUpdateVersion(update.Version.String())
return nil s.Qml.SetUpdateLandingPage(update.Landing)
s.Qml.SetUpdateReleaseNotesLink("https://protonmail.com/download/bridge/release_notes.html")
s.Qml.SetUpdateCanInstall(canInstall)
s.updateInfo = update
s.Qml.NotifyManualUpdate()
}
func (s *FrontendQt) NotifySilentUpdateInstalled() {
s.Qml.NotifySilentUpdateRestartNeeded()
}
func (s *FrontendQt) NotifySilentUpdateError(err error) {
s.Qml.NotifySilentUpdateError()
} }
func (s *FrontendQt) watchEvents() { func (s *FrontendQt) watchEvents() {
@ -233,7 +248,7 @@ func (s *FrontendQt) watchEvents() {
s.Qml.NotifyLogout(user.Username()) s.Qml.NotifyLogout(user.Username())
case <-updateApplicationCh: case <-updateApplicationCh:
s.Qml.ProcessFinished() s.Qml.ProcessFinished()
s.Qml.NotifyUpdate() s.Qml.NotifyForceUpdate()
case <-newUserCh: case <-newUserCh:
s.Qml.LoadAccounts() s.Qml.LoadAccounts()
case <-certIssue: case <-certIssue:
@ -343,6 +358,12 @@ func (s *FrontendQt) qtExecute(Procedure func(*FrontendQt) error) error {
s.Qml.SetIsAutoStart(false) s.Qml.SetIsAutoStart(false)
} }
if s.settings.GetBool(settings.AutoUpdateKey) {
s.Qml.SetIsAutoUpdate(true)
} else {
s.Qml.SetIsAutoUpdate(false)
}
if s.settings.GetBool(settings.AllowProxyKey) { if s.settings.GetBool(settings.AllowProxyKey) {
s.Qml.SetIsProxyAllowed(true) s.Qml.SetIsProxyAllowed(true)
} else { } else {
@ -397,16 +418,30 @@ func (s *FrontendQt) openLogs() {
go open.Run(logsPath) go open.Run(logsPath)
} }
// Check version in separate goroutine to not block the GUI (avoid program not responding message). func (s *FrontendQt) checkForUpdates() {
func (s *FrontendQt) isNewVersionAvailable(showMessage bool) {
go func() { go func() {
defer s.panicHandler.HandlePanic() version, err := s.updater.Check()
defer s.Qml.ProcessFinished()
s.Qml.SetConnectionStatus(true) // If we are here connection is ok. if err != nil {
s.Qml.SetUpdateState("upToDate") logrus.WithError(err).Error("An error occurred while checking updates manually")
if showMessage { s.Qml.NotifyManualUpdateError()
s.Qml.NotifyVersionIsTheLatest() return
} }
if !s.updater.IsUpdateApplicable(version) {
logrus.Debug("No need to update")
return
}
logrus.WithField("version", version.Version).Info("An update is available")
if !s.updater.CanInstall(version) {
logrus.Debug("A manual update is required")
s.NotifyManualUpdate(version, false)
return
}
s.NotifyManualUpdate(version, true)
}() }()
} }
@ -501,6 +536,18 @@ func (s *FrontendQt) toggleAutoStart() {
} }
} }
func (s *FrontendQt) toggleAutoUpdate() {
defer s.Qml.ProcessFinished()
if s.settings.GetBool(settings.AutoUpdateKey) {
s.settings.SetBool(settings.AutoUpdateKey, false)
s.Qml.SetIsAutoUpdate(false)
} else {
s.settings.SetBool(settings.AutoUpdateKey, true)
s.Qml.SetIsAutoUpdate(true)
}
}
func (s *FrontendQt) toggleAllowProxy() { func (s *FrontendQt) toggleAllowProxy() {
defer s.Qml.ProcessFinished() defer s.Qml.ProcessFinished()
@ -594,6 +641,15 @@ func (s *FrontendQt) saveOutgoingNoEncPopupCoord(x, y float32) {
//prefs.SetFloat(prefs.OutgoingNoEncPopupCoordY, y) //prefs.SetFloat(prefs.OutgoingNoEncPopupCoordY, y)
} }
func (s *FrontendQt) StartUpdate() { func (s *FrontendQt) startManualUpdate() {
// NOTE: Fix this. go func() {
err := s.updater.InstallUpdate(s.updateInfo)
if err != nil {
logrus.WithError(err).Error("An error occurred while installing updates manually")
s.Qml.NotifyManualUpdateError()
}
s.Qml.NotifyManualUpdateRestartNeeded()
}()
} }

View File

@ -43,9 +43,14 @@ func (s *FrontendHeadless) Loop() error {
return http.ListenAndServe(":8081", nil) return http.ListenAndServe(":8081", nil)
} }
func (s *FrontendHeadless) NotifyManualUpdate(update updater.VersionInfo) error { func (s *FrontendHeadless) NotifyManualUpdate(update updater.VersionInfo, canInstall bool) {
// NOTE: Save the update somewhere so that it can be installed when user chooses "install now". // NOTE: Save the update somewhere so that it can be installed when user chooses "install now".
return nil }
func (s *FrontendHeadless) NotifySilentUpdateInstalled() {
}
func (s *FrontendHeadless) NotifySilentUpdateError(err error) {
} }
func (s *FrontendHeadless) InstanceExistAlert() {} func (s *FrontendHeadless) InstanceExistAlert() {}

View File

@ -34,6 +34,7 @@ type GoQMLInterface struct {
_ func() `constructor:"init"` _ func() `constructor:"init"`
_ bool `property:"isAutoStart"` _ bool `property:"isAutoStart"`
_ bool `property:"isAutoUpdate"`
_ bool `property:"isProxyAllowed"` _ bool `property:"isProxyAllowed"`
_ string `property:"currentAddress"` _ string `property:"currentAddress"`
_ string `property:"goos"` _ string `property:"goos"`
@ -45,11 +46,21 @@ type GoQMLInterface struct {
_ bool `property:"isDefaultPort"` _ bool `property:"isDefaultPort"`
_ string `property:"programTitle"` _ string `property:"programTitle"`
_ string `property:"newversion"`
_ string `property:"fullversion"` _ string `property:"fullversion"`
_ string `property:"downloadLink"` _ string `property:"downloadLink"`
_ string `property:"landingPage"`
_ string `property:"releaseNotesLink"` _ string `property:"updateVersion"`
_ bool `property:"updateCanInstall"`
_ string `property:"updateLandingPage"`
_ string `property:"updateReleaseNotesLink"`
_ func() `signal:"notifyManualUpdate"`
_ func() `signal:"notifyManualUpdateRestartNeeded"`
_ func() `signal:"notifyManualUpdateError"`
_ func() `signal:"notifyForceUpdate"`
_ func() `signal:"notifySilentUpdateRestartNeeded"`
_ func() `signal:"notifySilentUpdateError"`
_ func() `slot:"checkForUpdates"`
_ func() `slot:"startManualUpdate"`
// Translations. // Translations.
_ string `property:"wrongCredentials"` _ string `property:"wrongCredentials"`
@ -82,6 +93,7 @@ type GoQMLInterface struct {
_ func() `signal:"showQuit"` _ func() `signal:"showQuit"`
_ func() `slot:"toggleAutoStart"` _ func() `slot:"toggleAutoStart"`
_ func() `slot:"toggleAutoUpdate"`
_ func() `slot:"toggleAllowProxy"` _ func() `slot:"toggleAllowProxy"`
_ func() `slot:"loadAccounts"` _ func() `slot:"loadAccounts"`
_ func() `slot:"openLogs"` _ func() `slot:"openLogs"`
@ -121,7 +133,6 @@ type GoQMLInterface struct {
_ func() `signal:"notifyVersionIsTheLatest"` _ func() `signal:"notifyVersionIsTheLatest"`
_ func() `signal:"notifyKeychainRebuild"` _ func() `signal:"notifyKeychainRebuild"`
_ func() `signal:"notifyHasNoKeychain"` _ func() `signal:"notifyHasNoKeychain"`
_ func() `signal:"notifyUpdate"`
_ func(accname string) `signal:"notifyLogout"` _ func(accname string) `signal:"notifyLogout"`
_ func(accname string) `signal:"notifyAddressChanged"` _ func(accname string) `signal:"notifyAddressChanged"`
_ func(accname string) `signal:"notifyAddressChangedLogout"` _ func(accname string) `signal:"notifyAddressChangedLogout"`
@ -137,7 +148,6 @@ type GoQMLInterface struct {
_ func(recipient string) `signal:"showNoActiveKeyForRecipient"` _ func(recipient string) `signal:"showNoActiveKeyForRecipient"`
_ func() `signal:"showCertIssue"` _ func() `signal:"showCertIssue"`
_ func() `slot:"startUpdate"`
_ func(hasError bool) `signal:"updateFinished"` _ func(hasError bool) `signal:"updateFinished"`
} }
@ -147,15 +157,16 @@ func (s *GoQMLInterface) init() {}
// SetFrontend connects all slots and signals from Go to QML. // SetFrontend connects all slots and signals from Go to QML.
func (s *GoQMLInterface) SetFrontend(f *FrontendQt) { func (s *GoQMLInterface) SetFrontend(f *FrontendQt) {
s.ConnectToggleAutoStart(f.toggleAutoStart) s.ConnectToggleAutoStart(f.toggleAutoStart)
s.ConnectToggleAutoUpdate(f.toggleAutoUpdate)
s.ConnectToggleAllowProxy(f.toggleAllowProxy) s.ConnectToggleAllowProxy(f.toggleAllowProxy)
s.ConnectLoadAccounts(f.loadAccounts) s.ConnectLoadAccounts(f.loadAccounts)
s.ConnectOpenLogs(f.openLogs) s.ConnectOpenLogs(f.openLogs)
s.ConnectClearCache(f.clearCache) s.ConnectClearCache(f.clearCache)
s.ConnectClearKeychain(f.clearKeychain) s.ConnectClearKeychain(f.clearKeychain)
s.ConnectOpenLicenseFile(f.openLicenseFile) s.ConnectOpenLicenseFile(f.openLicenseFile)
s.ConnectStartManualUpdate(f.startManualUpdate)
s.ConnectGetLocalVersionInfo(f.getLocalVersionInfo) s.ConnectGetLocalVersionInfo(f.getLocalVersionInfo)
s.ConnectIsNewVersionAvailable(f.isNewVersionAvailable) s.ConnectCheckForUpdates(f.checkForUpdates)
s.ConnectGetIMAPPort(f.getIMAPPort) s.ConnectGetIMAPPort(f.getIMAPPort)
s.ConnectGetSMTPPort(f.getSMTPPort) s.ConnectGetSMTPPort(f.getSMTPPort)
s.ConnectGetLastMailClient(f.getLastMailClient) s.ConnectGetLastMailClient(f.getLastMailClient)
@ -180,8 +191,6 @@ func (s *GoQMLInterface) SetFrontend(f *FrontendQt) {
s.SetGoos(runtime.GOOS) s.SetGoos(runtime.GOOS)
s.SetProgramTitle(f.programName) s.SetProgramTitle(f.programName)
s.SetReleaseNotesLink("https://protonmail.com/download/bridge/release_notes.html")
s.ConnectGetBackendVersion(func() string { s.ConnectGetBackendVersion(func() string {
return f.programVer return f.programVer
}) })
@ -193,5 +202,4 @@ func (s *GoQMLInterface) SetFrontend(f *FrontendQt) {
s.ConnectToggleIsReportingOutgoingNoEnc(f.toggleIsReportingOutgoingNoEnc) s.ConnectToggleIsReportingOutgoingNoEnc(f.toggleIsReportingOutgoingNoEnc)
s.ConnectShouldSendAnswer(f.shouldSendAnswer) s.ConnectShouldSendAnswer(f.shouldSendAnswer)
s.ConnectSaveOutgoingNoEncPopupCoord(f.saveOutgoingNoEncPopupCoord) s.ConnectSaveOutgoingNoEncPopupCoord(f.saveOutgoingNoEncPopupCoord)
s.ConnectStartUpdate(f.StartUpdate)
} }

View File

@ -41,7 +41,10 @@ type NoEncConfirmator interface {
} }
type Updater interface { type Updater interface {
Check() (updater.VersionInfo, error)
InstallUpdate(updater.VersionInfo) error InstallUpdate(updater.VersionInfo) error
IsUpdateApplicable(updater.VersionInfo) bool
CanInstall(updater.VersionInfo) bool
} }
// UserManager is an interface of users needed by frontend. // UserManager is an interface of users needed by frontend.

View File

@ -30,13 +30,13 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
type Installer struct{} type InstallerDarwin struct{}
func NewInstaller(*versioner.Versioner) *Installer { func NewInstaller(*versioner.Versioner) *InstallerDarwin {
return &Installer{} return &InstallerDarwin{}
} }
func (i *Installer) InstallUpdate(_ *semver.Version, r io.Reader) error { func (i *InstallerDarwin) InstallUpdate(_ *semver.Version, r io.Reader) error {
gr, err := gzip.NewReader(r) gr, err := gzip.NewReader(r)
if err != nil { if err != nil {
return err return err

View File

@ -26,16 +26,16 @@ import (
"github.com/ProtonMail/proton-bridge/internal/versioner" "github.com/ProtonMail/proton-bridge/internal/versioner"
) )
type Installer struct { type InstallerDefault struct {
versioner *versioner.Versioner versioner *versioner.Versioner
} }
func NewInstaller(versioner *versioner.Versioner) *Installer { func NewInstaller(versioner *versioner.Versioner) *InstallerDefault {
return &Installer{ return &InstallerDefault{
versioner: versioner, versioner: versioner,
} }
} }
func (i *Installer) InstallUpdate(version *semver.Version, r io.Reader) error { func (i *InstallerDefault) InstallUpdate(version *semver.Version, r io.Reader) error {
return i.versioner.InstallNewVersion(version, r) return i.versioner.InstallNewVersion(version, r)
} }

View File

@ -20,7 +20,6 @@ package updater
import ( import (
"encoding/json" "encoding/json"
"io" "io"
"time"
"github.com/Masterminds/semver/v3" "github.com/Masterminds/semver/v3"
"github.com/ProtonMail/gopenpgp/v2/crypto" "github.com/ProtonMail/gopenpgp/v2/crypto"
@ -29,17 +28,19 @@ import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
type clientProvider interface { var ErrManualUpdateRequired = errors.New("manual update is required")
type ClientProvider interface {
GetAnonymousClient() pmapi.Client GetAnonymousClient() pmapi.Client
} }
type installer interface { type Installer interface {
InstallUpdate(*semver.Version, io.Reader) error InstallUpdate(*semver.Version, io.Reader) error
} }
type Updater struct { type Updater struct {
cm clientProvider cm ClientProvider
installer installer installer Installer
kr *crypto.KeyRing kr *crypto.KeyRing
curVer *semver.Version curVer *semver.Version
@ -51,8 +52,8 @@ type Updater struct {
} }
func New( func New(
cm clientProvider, cm ClientProvider,
installer installer, installer Installer,
kr *crypto.KeyRing, kr *crypto.KeyRing,
curVer *semver.Version, curVer *semver.Version,
updateURLName, platform string, updateURLName, platform string,
@ -70,56 +71,44 @@ func New(
} }
} }
func (u *Updater) Watch( func (u *Updater) Check() (VersionInfo, error) {
period time.Duration,
handleUpdate func(VersionInfo) error,
handleError func(error),
) func() {
logrus.WithField("period", period).Info("Watching for updates")
ticker := time.NewTicker(period)
go func() {
for {
u.watch(handleUpdate, handleError)
<-ticker.C
}
}()
return ticker.Stop
}
func (u *Updater) watch(
handleUpdate func(VersionInfo) error,
handleError func(error),
) {
logrus.Info("Checking for updates") logrus.Info("Checking for updates")
latest, err := u.fetchVersionInfo() client := u.cm.GetAnonymousClient()
defer client.Logout()
r, err := client.DownloadAndVerify(
u.getVersionFileURL(),
u.getVersionFileURL()+".sig",
u.kr,
)
if err != nil { if err != nil {
handleError(errors.Wrap(err, "failed to fetch version info")) return VersionInfo{}, err
return
} }
if !latest.Version.GreaterThan(u.curVer) || u.rollout > latest.Rollout { var versionMap VersionMap
logrus.WithError(err).Debug("No need to update")
return if err := json.NewDecoder(r).Decode(&versionMap); err != nil {
return VersionInfo{}, err
} }
if u.curVer.LessThan(latest.MinAuto) { return versionMap[Channel], nil
logrus.Debug("A manual update is required") }
// NOTE: Need to notify user that they must update manually.
return func (u *Updater) IsUpdateApplicable(version VersionInfo) bool {
if !version.Version.GreaterThan(u.curVer) {
return false
} }
logrus. if u.rollout > version.Rollout {
WithField("latest", latest.Version). return false
WithField("current", u.curVer).
Info("An update is available")
if err := handleUpdate(latest); err != nil {
handleError(errors.Wrap(err, "failed to handle update"))
} }
return true
}
func (u *Updater) CanInstall(version VersionInfo) bool {
return !u.curVer.LessThan(version.MinAuto)
} }
func (u *Updater) InstallUpdate(update VersionInfo) error { func (u *Updater) InstallUpdate(update VersionInfo) error {
@ -143,25 +132,3 @@ func (u *Updater) InstallUpdate(update VersionInfo) error {
return nil return nil
}) })
} }
func (u *Updater) fetchVersionInfo() (VersionInfo, error) {
client := u.cm.GetAnonymousClient()
defer client.Logout()
r, err := client.DownloadAndVerify(
u.getVersionFileURL(),
u.getVersionFileURL()+".sig",
u.kr,
)
if err != nil {
return VersionInfo{}, err
}
var versionMap VersionMap
if err := json.NewDecoder(r).Decode(&versionMap); err != nil {
return VersionInfo{}, err
}
return versionMap[Channel], nil
}

View File

@ -34,13 +34,13 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestWatch(t *testing.T) { func TestCheck(t *testing.T) {
c := gomock.NewController(t) c := gomock.NewController(t)
defer c.Finish() defer c.Finish()
client := mocks.NewMockClient(c) client := mocks.NewMockClient(c)
updater := newTestUpdater(client, "1.4.0") updater := newTestUpdater(client, "1.1.0")
versionMap := VersionMap{ versionMap := VersionMap{
"live": VersionInfo{ "live": VersionInfo{
@ -59,119 +59,19 @@ func TestWatch(t *testing.T) {
client.EXPECT().Logout() client.EXPECT().Logout()
updateCh := make(chan VersionInfo) version, err := updater.Check()
defer updater.Watch( assert.Equal(t, semver.MustParse("1.5.0"), version.Version)
time.Minute, assert.NoError(t, err)
func(update VersionInfo) error {
updateCh <- update
return nil
},
func(err error) {
t.Fatal(err)
},
)()
assert.Equal(t, semver.MustParse("1.5.0"), (<-updateCh).Version)
} }
func TestWatchIgnoresCurrentVersion(t *testing.T) { func TestCheckBadSignature(t *testing.T) {
c := gomock.NewController(t) c := gomock.NewController(t)
defer c.Finish() defer c.Finish()
client := mocks.NewMockClient(c) client := mocks.NewMockClient(c)
updater := newTestUpdater(client, "1.5.0") updater := newTestUpdater(client, "1.2.0")
versionMap := VersionMap{
"live": VersionInfo{
Version: semver.MustParse("1.5.0"),
MinAuto: semver.MustParse("1.4.0"),
Package: "https://protonmail.com/download/bridge/update_1.5.0_linux.tgz",
Rollout: 1.0,
},
}
client.EXPECT().DownloadAndVerify(
updater.getVersionFileURL(),
updater.getVersionFileURL()+".sig",
gomock.Any(),
).Return(bytes.NewReader(mustMarshal(t, versionMap)), nil)
client.EXPECT().Logout()
updateCh := make(chan VersionInfo)
defer updater.Watch(
time.Minute,
func(update VersionInfo) error {
updateCh <- update
return nil
},
func(err error) {
t.Fatal(err)
},
)()
select {
case <-updateCh:
t.Fatal("We shouldn't update because we are already up to date")
case <-time.After(1500 * time.Millisecond):
}
}
func TestWatchIgnoresVerionsThatRequireManualUpdate(t *testing.T) {
c := gomock.NewController(t)
defer c.Finish()
client := mocks.NewMockClient(c)
updater := newTestUpdater(client, "1.4.0")
versionMap := VersionMap{
"live": VersionInfo{
Version: semver.MustParse("1.5.0"),
MinAuto: semver.MustParse("1.5.0"),
Package: "https://protonmail.com/download/bridge/update_1.5.0_linux.tgz",
Rollout: 1.0,
},
}
client.EXPECT().DownloadAndVerify(
updater.getVersionFileURL(),
updater.getVersionFileURL()+".sig",
gomock.Any(),
).Return(bytes.NewReader(mustMarshal(t, versionMap)), nil)
client.EXPECT().Logout()
updateCh := make(chan VersionInfo)
defer updater.Watch(
time.Minute,
func(update VersionInfo) error {
updateCh <- update
return nil
},
func(err error) {
t.Fatal(err)
},
)()
select {
case <-updateCh:
t.Fatal("We shouldn't update because this version requires a manual update")
case <-time.After(1500 * time.Millisecond):
}
}
func TestWatchBadSignature(t *testing.T) {
c := gomock.NewController(t)
defer c.Finish()
client := mocks.NewMockClient(c)
updater := newTestUpdater(client, "1.4.0")
client.EXPECT().DownloadAndVerify( client.EXPECT().DownloadAndVerify(
updater.getVersionFileURL(), updater.getVersionFileURL(),
@ -181,21 +81,72 @@ func TestWatchBadSignature(t *testing.T) {
client.EXPECT().Logout() client.EXPECT().Logout()
updateCh := make(chan VersionInfo) _, err := updater.Check()
errorsCh := make(chan error)
defer updater.Watch( assert.Error(t, err)
time.Minute, }
func(update VersionInfo) error {
updateCh <- update
return nil
},
func(err error) {
errorsCh <- err
},
)()
assert.Error(t, <-errorsCh) func TestIsUpdateApplicable(t *testing.T) {
c := gomock.NewController(t)
defer c.Finish()
client := mocks.NewMockClient(c)
updater := newTestUpdater(client, "1.4.0")
versionOld := VersionInfo{
Version: semver.MustParse("1.3.0"),
MinAuto: semver.MustParse("1.3.0"),
Package: "https://protonmail.com/download/bridge/update_1.3.0_linux.tgz",
Rollout: 1.0,
}
assert.Equal(t, false, updater.IsUpdateApplicable(versionOld))
versionEqual := VersionInfo{
Version: semver.MustParse("1.4.0"),
MinAuto: semver.MustParse("1.3.0"),
Package: "https://protonmail.com/download/bridge/update_1.4.0_linux.tgz",
Rollout: 1.0,
}
assert.Equal(t, false, updater.IsUpdateApplicable(versionEqual))
versionNew := VersionInfo{
Version: semver.MustParse("1.5.0"),
MinAuto: semver.MustParse("1.3.0"),
Package: "https://protonmail.com/download/bridge/update_1.5.0_linux.tgz",
Rollout: 1.0,
}
assert.Equal(t, true, updater.IsUpdateApplicable(versionNew))
}
func TestCanInstall(t *testing.T) {
c := gomock.NewController(t)
defer c.Finish()
client := mocks.NewMockClient(c)
updater := newTestUpdater(client, "1.4.0")
versionManual := VersionInfo{
Version: semver.MustParse("1.5.0"),
MinAuto: semver.MustParse("1.5.0"),
Package: "https://protonmail.com/download/bridge/update_1.5.0_linux.tgz",
Rollout: 1.0,
}
assert.Equal(t, false, updater.CanInstall(versionManual))
versionAuto := VersionInfo{
Version: semver.MustParse("1.5.0"),
MinAuto: semver.MustParse("1.3.0"),
Package: "https://protonmail.com/download/bridge/update_1.5.0_linux.tgz",
Rollout: 1.0,
}
assert.Equal(t, true, updater.CanInstall(versionAuto))
} }
func TestInstallUpdate(t *testing.T) { func TestInstallUpdate(t *testing.T) {
@ -221,7 +172,9 @@ func TestInstallUpdate(t *testing.T) {
client.EXPECT().Logout() client.EXPECT().Logout()
assert.NoError(t, updater.InstallUpdate(latestVersion)) err := updater.InstallUpdate(latestVersion)
assert.NoError(t, err)
} }
func TestInstallUpdateBadSignature(t *testing.T) { func TestInstallUpdateBadSignature(t *testing.T) {
@ -247,7 +200,9 @@ func TestInstallUpdateBadSignature(t *testing.T) {
client.EXPECT().Logout() client.EXPECT().Logout()
assert.Error(t, updater.InstallUpdate(latestVersion)) err := updater.InstallUpdate(latestVersion)
assert.Error(t, err)
} }
func TestInstallUpdateAlreadyOngoing(t *testing.T) { func TestInstallUpdateAlreadyOngoing(t *testing.T) {

View File

@ -5,6 +5,22 @@ Changelog [format](http://keepachangelog.com/en/1.0.0/)
## Unreleased ## Unreleased
### Added ### Added
* GODT-906 Handle RFC2047-encoded content transfer encoding values.
* GODT-875 Added GUI dialog on force update.
* GODT-820 Added GUI notification on impossibility of update installation (both silent and manual).
* GODT-870 Added GUI notification on error during silent update.
* GODT-805 Added GUI notification on update available.
* GODT-804 Added GUI notification on silent update installed (promt to restart).
* GODT-275 Added option to disable autoupdates in settings (default autoupdate is enabled).
* GODT-874 Added manual triggers to Updater module.
### Changed
* GODT-893 Bump go-rfc5322 dependency to v0.2.1 to properly detect syntax errors during parsing.
* GODT-892 Swap type and value from sentry exception and cut panic handlers from the traceback.
* GODT-854 EXPUNGE and FETCH unilateral responses are returned before OK EXPUNGE or OK STORE, respectively.
* GODT-806 Changed GUI dialog on manual update. Added autoupdates checkbox. Simplifyed installation process GUI.
### Removed ### Removed