[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-357 Use go-message to make a better message parser.
* GODT-720 Time measurement of progress for Import-Export.
* GODT-693 Launcher
* GODT-693 Launcher.
### 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/constants"
"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/smtp"
"github.com/ProtonMail/proton-bridge/internal/updater"
@ -119,19 +120,50 @@ func run(b *base.Base, c *cli.Context) error { // nolint[funlen]
b,
)
b.Updater.Watch(
time.Hour,
func(update updater.VersionInfo) error {
if !b.Settings.GetBool(settings.AutoUpdateKey) {
return f.NotifyManualUpdate(update)
}
// Watch for updates routine
go func() {
ticker := time.NewTicker(time.Hour)
return b.Updater.InstallUpdate(update)
},
func(err error) {
logrus.WithError(err).Error("An error occurred while watching for updates")
},
)
for {
checkAndHandleUpdate(b.Updater, f, b.Settings.GetBool(settings.AutoUpdateKey))
<-ticker.C
}
}()
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/constants"
"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/updater"
"github.com/sirupsen/logrus"
@ -59,25 +60,57 @@ func run(b *base.Base, c *cli.Context) error {
frontendMode,
b.CrashHandler,
b.Locations,
b.Settings,
b.Listener,
b.Updater,
ie,
b,
)
b.Updater.Watch(
time.Hour,
func(update updater.VersionInfo) error {
if !b.Settings.GetBool(settings.AutoUpdateKey) {
return f.NotifyManualUpdate(update)
}
// Watch for updates routine
go func() {
ticker := time.NewTicker(time.Hour)
return b.Updater.InstallUpdate(update)
},
func(err error) {
logrus.WithError(err).Error("An error occurred while watching for updates")
},
)
for {
checkAndHandleUpdate(b.Updater, f, b.Settings.GetBool(settings.AutoUpdateKey))
<-ticker.C
}
}()
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(AllowProxyKey, "true")
s.setDefault(AutostartKey, "true")
s.setDefault(AutoUpdateKey, "false")
s.setDefault(AutoUpdateKey, "true")
s.setDefault(ReportOutgoingNoEncKey, "false")
s.setDefault(LastVersionKey, "")
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
}
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".
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
}
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".
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).
type Frontend interface {
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`.
@ -123,8 +125,8 @@ func NewImportExport(
buildVersion,
frontendType string,
panicHandler types.PanicHandler,
locations *locations.Locations,
settings *settings.Settings,
eventListener listener.Listener,
updater types.Updater,
ie *importexport.ImportExport,
@ -137,6 +139,7 @@ func NewImportExport(
frontendType,
panicHandler,
locations,
settings,
eventListener,
updater,
ieWrap,
@ -149,8 +152,8 @@ func newIEFrontend(
buildVersion,
frontendType string,
panicHandler types.PanicHandler,
locations *locations.Locations,
settings *settings.Settings,
eventListener listener.Listener,
updater types.Updater,
ie types.ImportExporter,
@ -158,8 +161,25 @@ func newIEFrontend(
) Frontend {
switch frontendType {
case "cli":
return cliie.New(panicHandler, locations, eventListener, updater, ie, restarter)
return cliie.New(
panicHandler,
locations,
eventListener,
updater,
ie,
restarter,
)
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 == "instance exists" ) { 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.font.pointSize : Style.settings.toggleSize * Style.pt
onClicked: {
dialogGlobal.state="checkUpdates"
dialogGlobal.show()
dialogGlobal.confirmed()
go.checkForUpdates()
}
}
@ -138,7 +136,7 @@ Item {
fontSize : Style.main.fontSize
textUnderline : true
onClicked : {
Qt.openUrlExternally(go.releaseNotesLink)
Qt.openUrlExternally(go.updateReleaseNotesLink)
}
}
}

View File

@ -314,50 +314,7 @@ Window {
DialogUpdate {
id: dialogUpdate
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)
}
}
}
forceUpdate: root.isOutdateVersion
}

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 {
id: advancedSettings
property bool isAdvanced : !go.isDefaultPort

View File

@ -54,13 +54,15 @@ Item {
onWarningFlagsChanged : {
if (gui.warningFlags==Style.okInfoBar) {
go.normalSystray()
} else {
if ((gui.warningFlags & Style.errorInfoBar) == Style.errorInfoBar) {
go.errorSystray()
} else {
go.highlightSystray()
}
return
}
if ((gui.warningFlags & Style.errorInfoBar) == Style.errorInfoBar) {
go.errorSystray()
return
}
go.highlightSystray()
}
// 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 : {
// once app is outdated prevent from state change
if (winMain.updateState != "forceUpdate") {
@ -134,15 +128,50 @@ Item {
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")
if (!winMain.dialogUpdate.visible) {
gui.openMainWindow(true)
go.runCheckVersion(false)
winMain.dialogUpdate.show()
}
}
onNotifySilentUpdateRestartNeeded: {
go.setUpdateState("updateRestart")
gui.openMainWindow(true)
}
onNotifySilentUpdateError: {
go.setUpdateState("updateError")
gui.openMainWindow(true)
}
onNotifyLogout : {
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
}
onUpdateFinished : {
winMain.dialogUpdate.finished(hasError)
}
onShowCertIssue : {
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) {
// wait and check until font is loaded
while(true){
@ -301,10 +318,8 @@ Item {
// start window
gui.openMainWindow(false)
checkVersionTimer.start()
if (go.isShownOnStart) {
gui.winMain.showAndRise()
}
go.runCheckVersion(false)
}
}

View File

@ -38,7 +38,7 @@ Item {
property var allMonths : getMonthList(1,12)
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{}
@ -103,16 +103,9 @@ Item {
}
}
onRunCheckVersion : {
go.setUpdateState(gui.enums.statusUpToDate)
winMain.dialogGlobal.state=gui.enums.statusCheckingInternet
winMain.dialogGlobal.show()
go.isNewVersionAvailable(showMessage)
}
onSetUpdateState : {
// once app is outdated prevent from state change
if (winMain.updateState != gui.enums.statusForceUpdate) {
if (winMain.updateState != "forceUpdate") {
winMain.updateState = updateState
}
}
@ -213,13 +206,43 @@ Item {
}
}
onNotifyUpdate : {
go.setUpdateState("forceUpdate")
onNotifyManualUpdate: {
go.setUpdateState("oldVersion")
}
onNotifyManualUpdateRestartNeeded: {
if (!winMain.dialogUpdate.visible) {
gui.openMainWindow(true)
go.runCheckVersion(false)
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 : {
@ -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?")
// On start
Component.onCompleted : {
@ -402,9 +417,6 @@ Item {
go.bugNotSent = qsTr("Unable to submit bug report." , "notification", -1)
go.bugReportSent = qsTr("Bug report successfully sent." , "notification", -1)
go.runCheckVersion(false)
checkVersionTimer.start()
gui.allMonths = getMonthList(1,12)
gui.allMonthsChanged()
}

View File

@ -341,7 +341,7 @@ Dialog {
if ( state == "toggleAutoStart" ) { go.toggleAutoStart () }
if ( state == "quit" ) { 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.font.pointSize : Style.settings.toggleSize * Style.pt
onClicked: {
dialogGlobal.state="checkUpdates"
dialogGlobal.show()
dialogGlobal.confirmed()
go.checkForUpdates()
}
}
@ -130,7 +128,7 @@ Item {
MouseArea {
anchors.fill: parent
onClicked : {
Qt.openUrlExternally(go.releaseNotesLink)
Qt.openUrlExternally(go.updateReleaseNotesLink)
}
cursorShape: Qt.PointingHandCursor
}

View File

@ -55,7 +55,7 @@ Window {
minimumWidth : Style.main.width
minimumHeight : Style.main.height
property bool isOutdateVersion : root.updateState == "forceUpgrade"
property bool isOutdateVersion : root.updateState == "forceUpdate"
property bool activeContent :
!dialogAddUser .visible &&
@ -252,40 +252,7 @@ Window {
DialogUpdate {
id: dialogUpdate
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)
}
}
}
forceUpdate: root.isOutdateVersion
}

View File

@ -75,6 +75,25 @@ Item {
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 {

View File

@ -25,16 +25,17 @@ import ProtonUI 1.0
Dialog {
id: root
title: "Bridge update "+go.newversion
property alias introductionText : introduction.text
property bool hasError : false
property bool forceUpdate : false
signal cancel()
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
width: parent.width
@ -51,18 +52,45 @@ Dialog {
color: Style.dialog.text
linkColor: Style.dialog.textBlue
font {
pointSize: 0.8 * Style.dialog.fontSize * Style.pt
pointSize: Style.dialog.fontSize * Style.pt
}
width: 2*root.width/3
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap
// customize message per application
text: ' <a href="%1">Release notes</a><br> New version %2<br> <br><br> <a href="%3">%3</a>'
text: {
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 : {
console.log("clicked link:", link)
root.hide()
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 {
anchors.horizontalCenter: parent.horizontalCenter
spacing: Style.dialog.spacing
ButtonRounded {
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
onClicked: root.cancel()
onClicked: root.forceUpdate ? Qt.quit() : root.cancel()
}
ButtonRounded {
fa_icon: Style.fa.check
text: qsTr("Update")
visible: go.goos!="linux"
visible: go.updateCanInstall
color_main: Style.dialog.text
color_minor: Style.main.textBlue
isOpaque: true
@ -97,7 +134,7 @@ Dialog {
}
}
Rectangle { // 0: Check / download / unpack / prepare
Rectangle { // 1: Installing update
id: updateStatus
width: parent.width
height: parent.height
@ -116,35 +153,29 @@ Dialog {
width: 2*root.width/3
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap
text: {
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 ""
}
}
text: qsTr("Updating...")
}
ProgressBar {
id: progressbar
implicitWidth : 2*updateStatus.width/3
implicitHeight : Style.exporting.rowHeight
visible: go.progress!=0 // hack hide animation when clearing out progress bar
value: go.progress
property int current: go.total * go.progress
property bool isFinished: finishedPartBar.width == progressbar.width
id: updateProgressBar
width: 2*updateStatus.width/3
height: Style.exporting.rowHeight
//implicitWidth : 2*updateStatus.width/3
//implicitHeight : Style.exporting.rowHeight
indeterminate: true
//value: 0.5
//property int current: go.total * go.progress
//property bool isFinished: finishedPartBar.width == progressbar.width
background: Rectangle {
radius : Style.exporting.boxRadius
color : Style.exporting.progressBackground
}
contentItem: Item {
clip: true
Rectangle {
id: finishedPartBar
width : parent.width * progressbar.visualPosition
id: progressIndicator
width : updateProgressBar.indeterminate ? 50 : parent.width * updateProgressBar.visualPosition
height : parent.height
radius : Style.exporting.boxRadius
gradient : Gradient {
@ -156,6 +187,27 @@ Dialog {
Behavior on width {
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 {
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
height: parent.height
color: Style.transparent
@ -188,8 +240,8 @@ Dialog {
width: 2*root.width/3
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap
text: !root.hasError ? qsTr('Application will quit now to finish the update.', "message after successful update") :
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)
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.updateLandingPage)
onLinkActivated : {
console.log("clicked link:", link)
@ -220,7 +272,7 @@ Dialog {
function finished(hasError) {
root.hasError = hasError
root.incrementCurrentIndex()
root.currentIndex = 2
}
onShow: {
@ -234,9 +286,10 @@ Dialog {
onOkay: {
switch (root.currentIndex) {
case 0:
go.startUpdate()
go.startManualUpdate()
root.currentIndex = 1
break
}
root.incrementCurrentIndex()
}
onCancel: {

View File

@ -53,6 +53,7 @@ Rectangle {
}
Row {
id: messageRow
anchors.centerIn: root
visible: root.isVisible
spacing: Style.main.leftMarginButton
@ -63,80 +64,74 @@ Rectangle {
}
ClickIconText {
id: linkText
anchors.verticalCenter : message.verticalCenter
text : "("+go.newversion+" " + qsTr("release notes", "display the release notes from the new version")+")"
visible : root.state=="oldVersion"
iconText : ""
onClicked : {
Qt.openUrlExternally(go.releaseNotesLink)
}
fontSize : root.fontSize
}
ClickIconText {
id: actionText
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 : ""
onClicked : {
if (root.state=="oldVersion" || root.state=="forceUpdate" ) {
winMain.dialogUpdate.show()
} else {
go.checkInternet()
}
}
fontSize : root.fontSize
textUnderline: true
}
Text {
id: separatorText
anchors.baseline : message.baseline
color: Style.main.text
font {
pointSize : root.fontSize * Style.pt
bold : true
}
visible: root.state=="oldVersion" || root.state=="noInternet"
text : "|"
}
ClickIconText {
id: action2Text
anchors.verticalCenter : message.verticalCenter
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
textUnderline: true
}
}
ClickIconText {
id: closeSign
anchors.verticalCenter : messageRow.verticalCenter
anchors.right: root.right
iconText : Style.fa.close
fontSize : root.fontSize
textUnderline: true
}
onStateChanged : {
switch (root.state) {
case "forceUpdate" :
gui.warningFlags |= Style.errorInfoBar
break;
case "upToDate" :
gui.warningFlags &= ~Style.warnInfoBar
iTry = 0
secLeft=checkInterval[iTry]
break;
case "internetCheck":
break;
case "noInternet" :
gui.warningFlags |= Style.warnInfoBar
retryInternet.start()
secLeft=checkInterval[iTry]
break;
gui.warningFlags |= Style.warnInfoBar
retryInternet.start()
secLeft=checkInterval[iTry]
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 :
gui.warningFlags |= Style.warnInfoBar
break;
}
if (root.state!="noInternet") {
@ -172,6 +167,26 @@ Rectangle {
color: Style.main.background
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 {
name: "noInternet"
@ -186,6 +201,35 @@ Rectangle {
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()+"."
}
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 {
name: "oldVersion"
@ -198,7 +242,38 @@ Rectangle {
PropertyChanges {
target: message
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 {
@ -214,6 +289,30 @@ Rectangle {
color: Style.main.line
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 {
name: "upToDate"
@ -228,6 +327,103 @@ Rectangle {
color: Style.main.background
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 iconSize : 15 * px
property real leftMarginButton : 9 * px
property real verCheckRepeatTime : 15*60*60*1000 // milliseconds
property real topMargin : fontSize
property real bottomMargin : fontSize
property real border : 1 * px

View File

@ -108,9 +108,14 @@ Window {
ListElement { title: "Logout bridge" }
ListElement { title: "Internet on" }
ListElement { title: "Internet off" }
ListElement { title: "NeedUpdate" }
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: "Windows" }
ListElement { title: "Macos" }
@ -196,12 +201,29 @@ Window {
case "UpToDate" :
testroot.newVersion = false
break;
case "NeedUpdate" :
testroot.newVersion = true
break;
case "NotifyManualUpdate(CanInstall)" :
go.notifyManualUpdate()
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" :
go.notifyUpdate()
break;
go.notifyForceUpdate()
break;
case "NotifySilentUpdateRestartNeeded" :
go.notifySilentUpdateRestartNeeded()
break;
case "NotifySilentUpdateError" :
go.notifySilentUpdateError()
break;
case "SendAlertPopup" :
go.showOutgoingNoEncPopup("Alert sending unencrypted!")
break;
@ -244,6 +266,7 @@ Window {
id: go
property bool isAutoStart : true
property bool isAutoUpdate : false
property bool isProxyAllowed : false
property bool isFirstStart : false
property bool isFreshVersion : false
@ -269,17 +292,35 @@ Window {
property string genericErrSeeLogs
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 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 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 real progress: 0.3
property int progressDescription: 2
function setToRestart() {
console.log("setting to restart")
}
signal toggleMainWin(int systX, int systY, int systW, int systH)
@ -295,12 +336,11 @@ Window {
signal processFinished()
signal toggleAutoStart()
signal toggleAutoUpdate()
signal notifyBubble(int tabIndex, string message)
signal silentBubble(int tabIndex, string message)
signal runCheckVersion(bool showMessage)
signal setAddAccountWarning(string message)
signal notifyUpdate()
signal notifyFirewall()
signal notifyLogout(string accname)
signal notifyAddressChanged(string accname)
@ -463,9 +503,6 @@ Window {
switch (timer.work) {
case "wait":
break
case "startUpdate":
go.animateProgressBar.start()
go.updateFinished(true)
default:
go.processFinished()
}
@ -476,11 +513,6 @@ Window {
timer.start()
}
function startUpdate() {
timer.work="startUpdate"
timer.start()
}
function loadAccounts() {
console.log("Test: Account loaded")
}
@ -500,21 +532,7 @@ Window {
}
function getLocalVersionInfo(){
go.newversion = "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()
go.updateVersion = "QA.1.0"
}
function getBackendVersion() {
@ -603,6 +621,12 @@ Window {
isAutoStart = (isAutoStart!=false) ? false : true
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 {
id: buttons
ListElement { title : "Show window" }
ListElement { title : "Logout" }
ListElement { title : "Internet on" }
ListElement { title : "Internet off" }
ListElement { title : "Macos" }
ListElement { title : "Windows" }
ListElement { title : "Linux" }
ListElement { title : "New Version" }
ListElement { title : "ForceUpgrade" }
ListElement { title : "ImportStructure" }
ListElement { title : "DraftImpFailed" }
ListElement { title : "NoInterImp" }
ListElement { title : "ReportImp" }
ListElement { title : "NewFolder" }
ListElement { title : "EditFolder" }
ListElement { title : "EditLabel" }
ListElement { title : "ExpProgErr" }
ListElement { title : "ImpProgErr" }
ListElement { title : "Show window" }
ListElement { title : "Logout" }
ListElement { title : "Internet on" }
ListElement { title : "Internet off" }
ListElement { title : "Macos" }
ListElement { title : "Windows" }
ListElement { title : "Linux" }
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 : "ImportStructure" }
ListElement { title : "DraftImpFailed" }
ListElement { title : "NoInterImp" }
ListElement { title : "ReportImp" }
ListElement { title : "NewFolder" }
ListElement { title : "EditFolder" }
ListElement { title : "EditLabel" }
ListElement { title : "ExpProgErr" }
ListElement { title : "ImpProgErr" }
}
ListView {
@ -161,13 +166,29 @@ Window {
case "Linux" :
go.goos = "linux";
break;
case "New Version" :
testroot.newVersion = !testroot.newVersion
systrText.text = testroot.newVersion ? "new version" : "uptodate"
break
case "ForceUpgrade" :
go.notifyUpgrade()
break;
case "NotifyManualUpdate(CanInstall)" :
go.notifyManualUpdate()
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" :
go.notifyForceUpdate()
break;
case "NotifySilentUpdateRestartNeeded" :
go.notifySilentUpdateRestartNeeded()
break;
case "NotifySilentUpdateError" :
go.notifySilentUpdateError()
break;
case "ImportStructure" :
testgui.winMain.dialogImport.address = "cuto@pm.com"
testgui.winMain.dialogImport.show()
@ -815,6 +836,7 @@ Window {
id: go
property int isAutoStart : 1
property bool isAutoUpdate : false
property bool isFirstStart : false
property string currentAddress : "none"
//property string goos : "windows"
@ -831,9 +853,25 @@ Window {
property string bugReportSent
property string programTitle : "ProtonMail Import-Export app"
property string newversion : "q0.1.0"
property string landingPage : "https://landing.page"
property string releaseNotesLink : "https://protonmail.com/download/ie/release_notes.html"
property string fullversion : "QA.1.0 (d9f8sdf9) 2020-02-19T10:57:23+01:00"
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 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 int progressFails: 0
@ -846,13 +884,10 @@ Window {
signal toggleMainWin(int systX, int systY, int systW, int systH)
signal notifyHasNoKeychain()
signal notifyKeychainRebuild()
signal notifyAddressChangedLogout()
signal notifyAddressChanged()
signal notifyUpdate()
signal showWindow()
signal showHelp()
@ -871,10 +906,10 @@ Window {
signal processFinished()
signal toggleAutoStart()
signal toggleAutoUpdate()
signal notifyBubble(int tabIndex, string message)
signal runCheckVersion(bool showMessage)
signal setAddAccountWarning(string message)
signal notifyUpgrade()
signal notifyUpdate()
signal updateFinished(bool hasError)
signal notifyLogout(string accname)
@ -882,6 +917,10 @@ Window {
signal notifyError(int errCode)
property string errorDescription : ""
function setToRestart() {
console.log("setting to restart")
}
function delay(duration) {
var timeStart = new Date().getTime();
@ -955,7 +994,7 @@ Window {
workAndClose("addAccount")
}
property SequentialAnimation animateProgressBarUpgrade : SequentialAnimation {
property SequentialAnimation animateProgressBarUpdate : SequentialAnimation {
// version
PropertyAnimation{ target: go; properties: "progressDescription"; to: 1; duration: 1; }
PropertyAnimation{ duration: 2000; }
@ -1066,7 +1105,6 @@ Window {
onTriggered : {
console.log("triggered "+timer.work)
switch (timer.work) {
case "isNewVersionAvailable" :
case "clearCache" :
case "clearKeychain" :
case "logout" :
@ -1093,8 +1131,8 @@ Window {
go.animateProgressBar.start()
break;
case "startUpgrade":
go.animateProgressBarUpgrade.start()
case "startManualUpdate":
go.animateProgressBarUpdate.start()
go.updateFinished(true)
default:
@ -1105,18 +1143,10 @@ Window {
function workAndClose(workDescription) {
go.progress=0.0
timer.work = workDescription
timer.work = workDescription === undefined ? "" : workDescription
timer.start()
}
function startUpgrade() {
timer.work="startUpgrade"
timer.start()
}
function checkPathStatus(path) {
if ( path == "" ) return testgui.enums.pathEmptyPath
if ( path == "wrong" ) return testgui.enums.pathWrongPath
@ -1218,20 +1248,6 @@ Window {
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 getBackendVersion() {
@ -1328,5 +1344,11 @@ Window {
console.log("sending import report from ", address, " file ", fname)
return !fname.includes("fail")
}
onToggleAutoUpdate: {
workAndClose()
isAutoUpdate = (isAutoUpdate!=false) ? false : true
console.log (" Test: onToggleAutoUpdate "+isAutoUpdate)
}
}
}

View File

@ -23,6 +23,7 @@ import (
"errors"
"os"
"github.com/ProtonMail/proton-bridge/internal/config/settings"
"github.com/ProtonMail/proton-bridge/internal/events"
qtcommon "github.com/ProtonMail/proton-bridge/internal/frontend/qt-common"
"github.com/ProtonMail/proton-bridge/internal/frontend/types"
@ -50,6 +51,7 @@ var log = logrus.WithField("pkg", "frontend-qt-ie")
type FrontendQt struct {
panicHandler types.PanicHandler
locations *locations.Locations
settings *settings.Settings
eventListener listener.Listener
updater types.Updater
ie types.ImportExporter
@ -71,14 +73,17 @@ type FrontendQt struct {
progress *transfer.Progress
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
func New(
version, buildVersion string,
panicHandler types.PanicHandler,
locations *locations.Locations,
settings *settings.Settings,
eventListener listener.Listener,
updater types.Updater,
ie types.ImportExporter,
@ -87,6 +92,7 @@ func New(
f := &FrontendQt{
panicHandler: panicHandler,
locations: locations,
settings: settings,
programName: "ProtonMail Import-Export",
programVersion: "v" + version,
eventListener: eventListener,
@ -111,9 +117,21 @@ func (f *FrontendQt) Loop() (err error) {
return err
}
func (f *FrontendQt) NotifyManualUpdate(update updater.VersionInfo) error {
// NOTE: Save the update somewhere so that it can be installed when user chooses "install now".
return nil
func (f *FrontendQt) NotifyManualUpdate(update updater.VersionInfo, canInstall bool) {
f.Qml.SetUpdateVersion(update.Version.String())
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() {
@ -152,7 +170,7 @@ func (f *FrontendQt) watchEvents() {
f.Qml.NotifyLogout(user.Username())
case <-updateApplicationCh:
f.Qml.ProcessFinished()
f.Qml.NotifyUpdate()
f.Qml.NotifyForceUpdate()
case <-newUserCh:
f.Qml.LoadAccounts()
}
@ -219,6 +237,12 @@ func (f *FrontendQt) QtExecute(Procedure func(*FrontendQt) error) error {
f.Qml.SetCredits(importexport.Credits)
f.Qml.SetFullversion(f.buildVersion)
if f.settings.GetBool(settings.AutoUpdateKey) {
f.Qml.SetIsAutoUpdate(true)
} else {
f.Qml.SetIsAutoUpdate(false)
}
// Loop
if ret := gui.QGuiApplication_Exec(); ret != 0 {
//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
}
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
func (f *FrontendQt) checkInternet() {
f.Qml.SetConnectionStatus(f.ie.CheckConnection() == nil)
@ -369,21 +405,43 @@ func (f *FrontendQt) setProgressManager(progress *transfer.Progress) {
}()
}
func (f *FrontendQt) StartUpdate() {
// NOTE: Fix this.
func (f *FrontendQt) startManualUpdate() {
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
// return 0 when local version is fine
// return 1 when new version is available
func (f *FrontendQt) isNewVersionAvailable(showMessage bool) {
func (f *FrontendQt) checkForUpdates() {
go func() {
defer f.Qml.ProcessFinished()
f.Qml.SetConnectionStatus(true) // if we are here connection is ok
f.Qml.SetUpdateState(StatusUpToDate)
if showMessage {
f.Qml.NotifyVersionIsTheLatest()
version, err := f.updater.Check()
if err != nil {
logrus.WithError(err).Error("An error occurred while checking updates manually")
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"
"net/http"
"github.com/ProtonMail/proton-bridge/internal/config/settings"
"github.com/ProtonMail/proton-bridge/internal/frontend/types"
"github.com/ProtonMail/proton-bridge/internal/locations"
"github.com/ProtonMail/proton-bridge/internal/updater"
@ -42,15 +43,21 @@ func (s *FrontendHeadless) Loop() error {
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".
return nil
}
func (s *FrontendHeadless) NotifySilentUpdateInstalled() {
}
func (s *FrontendHeadless) NotifySilentUpdateError(err error) {
}
func New(
version, buildVersion string,
panicHandler types.PanicHandler,
locations *locations.Locations,
settings *settings.Settings,
eventListener listener.Listener,
updater types.Updater,
ie types.ImportExporter,

View File

@ -33,6 +33,7 @@ type GoQMLInterface struct {
_ func() `constructor:"init"`
_ bool `property:"isAutoUpdate"`
_ string `property:"currentAddress"`
_ string `property:"goos"`
_ string `property:"credits"`
@ -49,11 +50,21 @@ type GoQMLInterface struct {
_ string `property:importLogFileName`
_ string `property:"programTitle"`
_ string `property:"newversion"`
_ string `property:"fullversion"`
_ 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
_ string `property:"wrongCredentials"`
@ -79,6 +90,7 @@ type GoQMLInterface struct {
_ func() `signal:"showWindow"`
_ func() `slot:"toggleAutoUpdate"`
_ func() `slot:"quit"`
_ func() `slot:"loadAccounts"`
_ func() `slot:"openLogs"`
@ -89,8 +101,7 @@ type GoQMLInterface struct {
_ func() `signal:"highlightSystray"`
_ 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(address string) bool `slot:"sendImportReport"`
@ -128,12 +139,10 @@ type GoQMLInterface struct {
_ func() `signal:"notifyVersionIsTheLatest"`
_ func() `signal:"notifyKeychainRebuild"`
_ func() `signal:"notifyHasNoKeychain"`
_ func() `signal:"notifyUpdate"`
_ func(accname string) `signal:"notifyLogout"`
_ func(accname string) `signal:"notifyAddressChanged"`
_ func(accname string) `signal:"notifyAddressChangedLogout"`
_ func() `slot:"startUpdate"`
_ func(hasError bool) `signal:"updateFinished"`
// errors
@ -150,6 +159,7 @@ func (s *GoQMLInterface) init() {}
func (s *GoQMLInterface) SetFrontend(f *FrontendQt) {
s.ConnectQuit(f.App.Quit)
s.ConnectToggleAutoUpdate(f.toggleAutoUpdate)
s.ConnectLoadAccounts(f.Accounts.LoadAccounts)
s.ConnectOpenLogs(f.openLogs)
s.ConnectOpenDownloadLink(f.openDownloadLink)
@ -170,10 +180,10 @@ func (s *GoQMLInterface) SetFrontend(f *FrontendQt) {
s.SetProgramTitle(f.programName)
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.ConnectIsNewVersionAvailable(f.isNewVersionAvailable)
s.ConnectCheckForUpdates(f.checkForUpdates)
s.ConnectGetBackendVersion(func() string {
return f.programVersion
})
@ -193,7 +203,5 @@ func (s *GoQMLInterface) SetFrontend(f *FrontendQt) {
s.ConnectCheckPathStatus(CheckPathStatus)
s.ConnectStartUpdate(f.StartUpdate)
s.ConnectEmitEvent(f.emitEvent)
}

View File

@ -95,6 +95,9 @@ type FrontendQt struct {
userIDAdded string
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.
@ -173,9 +176,21 @@ func (s *FrontendQt) Loop() (err error) {
return err
}
func (s *FrontendQt) NotifyManualUpdate(update updater.VersionInfo) error {
// NOTE: Save the update somewhere so that it can be installed when user chooses "install now".
return nil
func (s *FrontendQt) NotifyManualUpdate(update updater.VersionInfo, canInstall bool) {
s.Qml.SetUpdateVersion(update.Version.String())
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() {
@ -233,7 +248,7 @@ func (s *FrontendQt) watchEvents() {
s.Qml.NotifyLogout(user.Username())
case <-updateApplicationCh:
s.Qml.ProcessFinished()
s.Qml.NotifyUpdate()
s.Qml.NotifyForceUpdate()
case <-newUserCh:
s.Qml.LoadAccounts()
case <-certIssue:
@ -343,6 +358,12 @@ func (s *FrontendQt) qtExecute(Procedure func(*FrontendQt) error) error {
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) {
s.Qml.SetIsProxyAllowed(true)
} else {
@ -397,16 +418,30 @@ func (s *FrontendQt) openLogs() {
go open.Run(logsPath)
}
// Check version in separate goroutine to not block the GUI (avoid program not responding message).
func (s *FrontendQt) isNewVersionAvailable(showMessage bool) {
func (s *FrontendQt) checkForUpdates() {
go func() {
defer s.panicHandler.HandlePanic()
defer s.Qml.ProcessFinished()
s.Qml.SetConnectionStatus(true) // If we are here connection is ok.
s.Qml.SetUpdateState("upToDate")
if showMessage {
s.Qml.NotifyVersionIsTheLatest()
version, err := s.updater.Check()
if err != nil {
logrus.WithError(err).Error("An error occurred while checking updates manually")
s.Qml.NotifyManualUpdateError()
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() {
defer s.Qml.ProcessFinished()
@ -594,6 +641,15 @@ func (s *FrontendQt) saveOutgoingNoEncPopupCoord(x, y float32) {
//prefs.SetFloat(prefs.OutgoingNoEncPopupCoordY, y)
}
func (s *FrontendQt) StartUpdate() {
// NOTE: Fix this.
func (s *FrontendQt) startManualUpdate() {
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)
}
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".
return nil
}
func (s *FrontendHeadless) NotifySilentUpdateInstalled() {
}
func (s *FrontendHeadless) NotifySilentUpdateError(err error) {
}
func (s *FrontendHeadless) InstanceExistAlert() {}

View File

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

View File

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

View File

@ -30,13 +30,13 @@ import (
"github.com/pkg/errors"
)
type Installer struct{}
type InstallerDarwin struct{}
func NewInstaller(*versioner.Versioner) *Installer {
return &Installer{}
func NewInstaller(*versioner.Versioner) *InstallerDarwin {
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)
if err != nil {
return err

View File

@ -26,16 +26,16 @@ import (
"github.com/ProtonMail/proton-bridge/internal/versioner"
)
type Installer struct {
type InstallerDefault struct {
versioner *versioner.Versioner
}
func NewInstaller(versioner *versioner.Versioner) *Installer {
return &Installer{
func NewInstaller(versioner *versioner.Versioner) *InstallerDefault {
return &InstallerDefault{
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)
}

View File

@ -20,7 +20,6 @@ package updater
import (
"encoding/json"
"io"
"time"
"github.com/Masterminds/semver/v3"
"github.com/ProtonMail/gopenpgp/v2/crypto"
@ -29,17 +28,19 @@ import (
"github.com/sirupsen/logrus"
)
type clientProvider interface {
var ErrManualUpdateRequired = errors.New("manual update is required")
type ClientProvider interface {
GetAnonymousClient() pmapi.Client
}
type installer interface {
type Installer interface {
InstallUpdate(*semver.Version, io.Reader) error
}
type Updater struct {
cm clientProvider
installer installer
cm ClientProvider
installer Installer
kr *crypto.KeyRing
curVer *semver.Version
@ -51,8 +52,8 @@ type Updater struct {
}
func New(
cm clientProvider,
installer installer,
cm ClientProvider,
installer Installer,
kr *crypto.KeyRing,
curVer *semver.Version,
updateURLName, platform string,
@ -70,56 +71,44 @@ func New(
}
}
func (u *Updater) Watch(
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),
) {
func (u *Updater) Check() (VersionInfo, error) {
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 {
handleError(errors.Wrap(err, "failed to fetch version info"))
return
return VersionInfo{}, err
}
if !latest.Version.GreaterThan(u.curVer) || u.rollout > latest.Rollout {
logrus.WithError(err).Debug("No need to update")
return
var versionMap VersionMap
if err := json.NewDecoder(r).Decode(&versionMap); err != nil {
return VersionInfo{}, err
}
if u.curVer.LessThan(latest.MinAuto) {
logrus.Debug("A manual update is required")
// NOTE: Need to notify user that they must update manually.
return
return versionMap[Channel], nil
}
func (u *Updater) IsUpdateApplicable(version VersionInfo) bool {
if !version.Version.GreaterThan(u.curVer) {
return false
}
logrus.
WithField("latest", latest.Version).
WithField("current", u.curVer).
Info("An update is available")
if err := handleUpdate(latest); err != nil {
handleError(errors.Wrap(err, "failed to handle update"))
if u.rollout > version.Rollout {
return false
}
return true
}
func (u *Updater) CanInstall(version VersionInfo) bool {
return !u.curVer.LessThan(version.MinAuto)
}
func (u *Updater) InstallUpdate(update VersionInfo) error {
@ -143,25 +132,3 @@ func (u *Updater) InstallUpdate(update VersionInfo) error {
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"
)
func TestWatch(t *testing.T) {
func TestCheck(t *testing.T) {
c := gomock.NewController(t)
defer c.Finish()
client := mocks.NewMockClient(c)
updater := newTestUpdater(client, "1.4.0")
updater := newTestUpdater(client, "1.1.0")
versionMap := VersionMap{
"live": VersionInfo{
@ -59,119 +59,19 @@ func TestWatch(t *testing.T) {
client.EXPECT().Logout()
updateCh := make(chan VersionInfo)
version, err := updater.Check()
defer updater.Watch(
time.Minute,
func(update VersionInfo) error {
updateCh <- update
return nil
},
func(err error) {
t.Fatal(err)
},
)()
assert.Equal(t, semver.MustParse("1.5.0"), (<-updateCh).Version)
assert.Equal(t, semver.MustParse("1.5.0"), version.Version)
assert.NoError(t, err)
}
func TestWatchIgnoresCurrentVersion(t *testing.T) {
func TestCheckBadSignature(t *testing.T) {
c := gomock.NewController(t)
defer c.Finish()
client := mocks.NewMockClient(c)
updater := newTestUpdater(client, "1.5.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")
updater := newTestUpdater(client, "1.2.0")
client.EXPECT().DownloadAndVerify(
updater.getVersionFileURL(),
@ -181,21 +81,72 @@ func TestWatchBadSignature(t *testing.T) {
client.EXPECT().Logout()
updateCh := make(chan VersionInfo)
errorsCh := make(chan error)
_, err := updater.Check()
defer updater.Watch(
time.Minute,
func(update VersionInfo) error {
updateCh <- update
return nil
},
func(err error) {
errorsCh <- err
},
)()
assert.Error(t, 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) {
@ -221,7 +172,9 @@ func TestInstallUpdate(t *testing.T) {
client.EXPECT().Logout()
assert.NoError(t, updater.InstallUpdate(latestVersion))
err := updater.InstallUpdate(latestVersion)
assert.NoError(t, err)
}
func TestInstallUpdateBadSignature(t *testing.T) {
@ -247,7 +200,9 @@ func TestInstallUpdateBadSignature(t *testing.T) {
client.EXPECT().Logout()
assert.Error(t, updater.InstallUpdate(latestVersion))
err := updater.InstallUpdate(latestVersion)
assert.Error(t, err)
}
func TestInstallUpdateAlreadyOngoing(t *testing.T) {

View File

@ -5,6 +5,22 @@ Changelog [format](http://keepachangelog.com/en/1.0.0/)
## Unreleased
### 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