mirror of
https://git.jami.net/savoirfairelinux/jami-client-qt.git
synced 2025-07-16 21:45:24 +02:00
windows: updates: make UI represent underlying update states
This commit fixes several issues: - the update confirmation dialog getting stuck in the open state - secondary attempts to download an update possibly causing a crash - poor error information presented in the case of missing content - unhandled errors during MSI package installation Gitlab: #1367 Change-Id: Ia8855b8268ab13b8e1cbbb15de75d41f593f947a
This commit is contained in:
parent
2719f303d9
commit
6fc30b51d6
8 changed files with 132 additions and 94 deletions
|
@ -268,7 +268,7 @@ ApplicationWindow {
|
|||
}
|
||||
|
||||
function presentUpdateInfoDialog(infoText) {
|
||||
viewCoordinator.presentDialog(appWindow, "commoncomponents/SimpleMessageDialog.qml", {
|
||||
return viewCoordinator.presentDialog(appWindow, "commoncomponents/SimpleMessageDialog.qml", {
|
||||
"title": JamiStrings.updateDialogTitle,
|
||||
"infoText": infoText,
|
||||
"buttonTitles": [JamiStrings.optionOk],
|
||||
|
@ -277,6 +277,36 @@ ApplicationWindow {
|
|||
});
|
||||
}
|
||||
|
||||
function presentUpdateConfirmInstallDialog(switchToBeta=false) {
|
||||
return viewCoordinator.presentDialog(appWindow, "commoncomponents/SimpleMessageDialog.qml", {
|
||||
"title": JamiStrings.updateDialogTitle,
|
||||
"infoText": switchToBeta ? JamiStrings.confirmBeta : JamiStrings.updateFound,
|
||||
"buttonTitles": [JamiStrings.optionUpgrade, JamiStrings.optionLater],
|
||||
"buttonStyles": [SimpleMessageDialog.ButtonStyle.TintedBlue, SimpleMessageDialog.ButtonStyle.TintedBlue],
|
||||
"buttonCallBacks": [function () {
|
||||
AppVersionManager.applyUpdates(switchToBeta);
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
function translateErrorToString(error) {
|
||||
switch (error) {
|
||||
case NetworkManager.DISCONNECTED:
|
||||
return JamiStrings.networkDisconnected;
|
||||
case NetworkManager.CONTENT_NOT_FOUND:
|
||||
return JamiStrings.contentNotFoundError;
|
||||
case NetworkManager.ACCESS_DENIED:
|
||||
return JamiStrings.accessError;
|
||||
case NetworkManager.SSL_ERROR:
|
||||
return JamiStrings.updateSSLError;
|
||||
case NetworkManager.CANCELED:
|
||||
return JamiStrings.updateDownloadCanceled;
|
||||
case NetworkManager.NETWORK_ERROR:
|
||||
default:
|
||||
return JamiStrings.updateNetworkError;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: AppVersionManager
|
||||
|
||||
|
@ -288,41 +318,26 @@ ApplicationWindow {
|
|||
|
||||
function onUpdateCheckReplyReceived(ok, found) {
|
||||
if (!ok) {
|
||||
// Show an error dialog describing that we could not successfully check for an update.
|
||||
presentUpdateInfoDialog(JamiStrings.updateCheckError);
|
||||
return;
|
||||
}
|
||||
if (!found) {
|
||||
// Show a dialog describing that no update was found.
|
||||
presentUpdateInfoDialog(JamiStrings.updateNotFound);
|
||||
} else {
|
||||
viewCoordinator.presentDialog(appWindow, "commoncomponents/SimpleMessageDialog.qml", {
|
||||
"title": JamiStrings.updateDialogTitle,
|
||||
"infoText": JamiStrings.updateFound,
|
||||
"buttonTitles": [JamiStrings.optionUpgrade, JamiStrings.optionLater],
|
||||
"buttonStyles": [SimpleMessageDialog.ButtonStyle.TintedBlue, SimpleMessageDialog.ButtonStyle.TintedBlue],
|
||||
"buttonCallBacks": [function () {
|
||||
AppVersionManager.applyUpdates();
|
||||
}]
|
||||
});
|
||||
// Show a dialog describing that an update were found, and offering to install it.
|
||||
presentUpdateConfirmInstallDialog()
|
||||
}
|
||||
}
|
||||
|
||||
function onUpdateErrorOccurred(error) {
|
||||
presentUpdateInfoDialog((function () {
|
||||
switch (error) {
|
||||
case NetworkManager.ACCESS_DENIED:
|
||||
return JamiStrings.genericError;
|
||||
case NetworkManager.DISCONNECTED:
|
||||
return JamiStrings.networkDisconnected;
|
||||
case NetworkManager.NETWORK_ERROR:
|
||||
return JamiStrings.updateNetworkError;
|
||||
case NetworkManager.SSL_ERROR:
|
||||
return JamiStrings.updateSSLError;
|
||||
case NetworkManager.CANCELED:
|
||||
return JamiStrings.updateDownloadCanceled;
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
})());
|
||||
function onNetworkErrorOccurred(error) {
|
||||
var errorStr = translateErrorToString(error);
|
||||
presentUpdateInfoDialog(errorStr);
|
||||
}
|
||||
|
||||
function onInstallErrorOccurred(errorMsg) {
|
||||
presentUpdateInfoDialog(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -62,11 +62,11 @@ struct AppVersionManager::Impl : public QObject
|
|||
connect(&parent_,
|
||||
&NetworkManager::errorOccurred,
|
||||
&parent_,
|
||||
&AppVersionManager::updateErrorOccurred);
|
||||
&AppVersionManager::networkErrorOccurred);
|
||||
|
||||
cleanUpdateFiles();
|
||||
QUrl versionUrl {isBeta ? QUrl::fromUserInput(baseUrlString_ + betaVersionSubUrl)
|
||||
: QUrl::fromUserInput(baseUrlString_ + versionSubUrl)};
|
||||
const QUrl versionUrl {isBeta ? QUrl::fromUserInput(baseUrlString_ + betaVersionSubUrl)
|
||||
: QUrl::fromUserInput(baseUrlString_ + versionSubUrl)};
|
||||
parent_.sendGetRequest(versionUrl, [this, quiet](const QByteArray& latestVersionString) {
|
||||
if (latestVersionString.isEmpty()) {
|
||||
qWarning() << "Error checking version";
|
||||
|
@ -76,14 +76,15 @@ struct AppVersionManager::Impl : public QObject
|
|||
}
|
||||
auto currentVersion = QString(VERSION_STRING).toULongLong();
|
||||
auto latestVersion = latestVersionString.toULongLong();
|
||||
qDebug() << "latest: " << latestVersion << " current: " << currentVersion;
|
||||
if (latestVersion > currentVersion) {
|
||||
qDebug() << "New version found";
|
||||
const QString channelStr = isBeta ? "beta" : "stable";
|
||||
const auto newVersionFound = latestVersion > currentVersion;
|
||||
qInfo().noquote() << "--------- Version info ------------"
|
||||
<< QString("\n - Current: %1 (%2)").arg(currentVersion).arg(channelStr);
|
||||
if (newVersionFound) {
|
||||
qDebug() << " - Latest: " << latestVersion;
|
||||
Q_EMIT parent_.updateCheckReplyReceived(true, true);
|
||||
} else {
|
||||
qDebug() << "No new version found";
|
||||
if (!quiet)
|
||||
Q_EMIT parent_.updateCheckReplyReceived(true, false);
|
||||
} else if (!quiet) {
|
||||
Q_EMIT parent_.updateCheckReplyReceived(true, false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -94,35 +95,56 @@ struct AppVersionManager::Impl : public QObject
|
|||
connect(&parent_,
|
||||
&NetworkManager::errorOccurred,
|
||||
&parent_,
|
||||
&AppVersionManager::updateErrorOccurred);
|
||||
&AppVersionManager::networkErrorOccurred);
|
||||
|
||||
const QUrl downloadUrl {(beta || isBeta)
|
||||
? QUrl::fromUserInput(baseUrlString_ + betaMsiSubUrl)
|
||||
: QUrl::fromUserInput(baseUrlString_ + msiSubUrl)};
|
||||
|
||||
int uuid = parent_.downloadFile(
|
||||
const auto lastDownloadReplyId = parent_.replyId_;
|
||||
parent_.replyId_ = parent_.downloadFile(
|
||||
downloadUrl,
|
||||
*(parent_.replyId_),
|
||||
lastDownloadReplyId,
|
||||
[this, downloadUrl](bool success, const QString& errorMessage) {
|
||||
Q_UNUSED(success)
|
||||
Q_UNUSED(errorMessage)
|
||||
const QProcess process;
|
||||
QProcess process;
|
||||
auto basePath = tempPath_ + QDir::separator();
|
||||
auto msiPath = QDir::toNativeSeparators(basePath + downloadUrl.fileName());
|
||||
auto logPath = QDir::toNativeSeparators(basePath + "jami_x64_install.log");
|
||||
process.startDetached("msiexec",
|
||||
QStringList() << "/i" << msiPath << "/passive"
|
||||
<< "/norestart"
|
||||
<< "WIXNONUILAUNCH=1"
|
||||
<< "/L*V" << logPath);
|
||||
connect(&process, &QProcess::errorOccurred, this, [&](QProcess::ProcessError error) {
|
||||
QString errorMsg;
|
||||
if (error == QProcess::ProcessError::Timedout) {
|
||||
errorMsg = tr("The installer process has timed out.");
|
||||
} else {
|
||||
errorMsg = process.readAllStandardError();
|
||||
if (errorMsg.isEmpty())
|
||||
errorMsg = tr("The installer process has failed.");
|
||||
}
|
||||
Q_EMIT parent_.installErrorOccurred(errorMsg);
|
||||
});
|
||||
connect(&process,
|
||||
&QProcess::finished,
|
||||
this,
|
||||
[&](int exitCode, QProcess::ExitStatus exitStatus) {
|
||||
if (exitStatus != QProcess::ExitStatus::NormalExit || exitCode != 0) {
|
||||
auto errorMsg = process.readAllStandardOutput();
|
||||
Q_EMIT parent_.installErrorOccurred(errorMsg);
|
||||
}
|
||||
});
|
||||
process.start("msiexec",
|
||||
QStringList() << "/i" << msiPath << "/passive"
|
||||
<< "/norestart"
|
||||
<< "WIXNONUILAUNCH=1"
|
||||
<< "/L*V" << logPath);
|
||||
process.waitForFinished();
|
||||
},
|
||||
tempPath_);
|
||||
parent_.replyId_.reset(&uuid);
|
||||
};
|
||||
|
||||
void cancelUpdate()
|
||||
{
|
||||
parent_.cancelDownload(*(parent_.replyId_));
|
||||
parent_.cancelDownload(parent_.replyId_);
|
||||
};
|
||||
|
||||
void setAutoUpdateCheck(bool state)
|
||||
|
@ -138,7 +160,7 @@ struct AppVersionManager::Impl : public QObject
|
|||
void cleanUpdateFiles()
|
||||
{
|
||||
// Delete all logs and msi in the temporary directory before launching.
|
||||
QString dir = QDir::tempPath();
|
||||
const QString dir = QDir::tempPath();
|
||||
QDir log_dir(dir, {"jami*.log"});
|
||||
for (const QString& filename : log_dir.entryList()) {
|
||||
log_dir.remove(filename);
|
||||
|
@ -166,13 +188,13 @@ AppVersionManager::AppVersionManager(const QString& url,
|
|||
LRCInstance* instance,
|
||||
QObject* parent)
|
||||
: NetworkManager(cm, parent)
|
||||
, replyId_(new int(0))
|
||||
, replyId_(0)
|
||||
, pimpl_(std::make_unique<Impl>(url, instance, *this))
|
||||
{}
|
||||
|
||||
AppVersionManager::~AppVersionManager()
|
||||
{
|
||||
cancelDownload(*replyId_);
|
||||
cancelDownload(replyId_);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -47,10 +47,11 @@ Q_SIGNALS:
|
|||
void appCloseRequested();
|
||||
void updateCheckReplyReceived(bool ok, bool found = false);
|
||||
void updateDownloadProgressChanged(qint64 bytesRead, qint64 totalBytes);
|
||||
void updateErrorOccurred(const NetworkManager::GetError& error);
|
||||
void networkErrorOccurred(const NetworkManager::GetError& error);
|
||||
void installErrorOccurred(const QString& errorMsg);
|
||||
|
||||
private:
|
||||
QScopedPointer<int> replyId_;
|
||||
int replyId_;
|
||||
struct Impl;
|
||||
friend struct Impl;
|
||||
std::unique_ptr<Impl> pimpl_;
|
||||
|
|
|
@ -513,6 +513,8 @@ Item {
|
|||
property string updateDownloading: "Downloading"
|
||||
property string confirmBeta: qsTr("This will uninstall your current Release version and you can always download the latest Release version on our website")
|
||||
property string networkDisconnected: qsTr("Network disconnected")
|
||||
property string accessError: qsTr("Content access error")
|
||||
property string contentNotFoundError: qsTr("Content not found")
|
||||
property string genericError: qsTr("Something went wrong")
|
||||
|
||||
//Troubleshoot Settings
|
||||
|
|
|
@ -25,6 +25,28 @@
|
|||
#include <QtNetwork>
|
||||
#include <QScopedPointer>
|
||||
|
||||
namespace {
|
||||
NetworkManager::GetError
|
||||
translateErrorCode(QNetworkReply::NetworkError error)
|
||||
{
|
||||
// From qnetworkreply.h:
|
||||
// network layer errors (1-99): / proxy errors (101-199):
|
||||
// content errors (201-299): ContentAccessDenied = 201,
|
||||
// protocol errors / Server side errors (401-499)
|
||||
static auto inRange = [](int value, int min, int max) -> bool {
|
||||
return (value >= min && value <= max);
|
||||
};
|
||||
if (inRange(error, 1, 199))
|
||||
return NetworkManager::NETWORK_ERROR;
|
||||
if (inRange(error, 201, 201))
|
||||
return NetworkManager::ACCESS_DENIED;
|
||||
if (inRange(error, 202, 299))
|
||||
return NetworkManager::CONTENT_NOT_FOUND;
|
||||
return NetworkManager::NETWORK_ERROR;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
NetworkManager::NetworkManager(ConnectivityMonitor* cm, QObject* parent)
|
||||
: QObject(parent)
|
||||
, manager_(new QNetworkAccessManager(this))
|
||||
|
@ -45,7 +67,7 @@ NetworkManager::NetworkManager(ConnectivityMonitor* cm, QObject* parent)
|
|||
});
|
||||
#endif
|
||||
connect(connectivityMonitor_, &ConnectivityMonitor::connectivityChanged, this, [this] {
|
||||
auto connected = connectivityMonitor_->isOnline();
|
||||
const auto connected = connectivityMonitor_->isOnline();
|
||||
if (connected && !lastConnectionState_) {
|
||||
manager_->deleteLater();
|
||||
manager_ = new QNetworkAccessManager(this);
|
||||
|
@ -59,7 +81,7 @@ void
|
|||
NetworkManager::sendGetRequest(const QUrl& url,
|
||||
std::function<void(const QByteArray&)>&& onDoneCallback)
|
||||
{
|
||||
QNetworkRequest request = QNetworkRequest(url);
|
||||
const QNetworkRequest request = QNetworkRequest(url);
|
||||
sendGetRequest(request, std::move(onDoneCallback));
|
||||
}
|
||||
|
||||
|
@ -98,9 +120,8 @@ NetworkManager::downloadFile(const QUrl& url,
|
|||
const QString& filePath,
|
||||
const QString& extension)
|
||||
{
|
||||
// If there is already a download in progress, return.
|
||||
if ((downloadReplies_.value(replyId) != NULL || !(replyId == 0))
|
||||
&& downloadReplies_[replyId]->isRunning()) {
|
||||
// Don't replace the download if there is already a download in progress for this id.
|
||||
if (downloadReplies_.contains(replyId) && downloadReplies_.value(replyId)->isRunning()) {
|
||||
qWarning() << Q_FUNC_INFO << "Download already in progress";
|
||||
return replyId;
|
||||
}
|
||||
|
@ -121,6 +142,7 @@ NetworkManager::downloadFile(const QUrl& url,
|
|||
}
|
||||
|
||||
// set the id for the request
|
||||
// NOLINTNEXTLINE(misc-const-correctness)
|
||||
std::uniform_int_distribution<int> dist(1, std::numeric_limits<int>::max());
|
||||
auto uuid = dist(rng_);
|
||||
|
||||
|
@ -167,7 +189,7 @@ NetworkManager::downloadFile(const QUrl& url,
|
|||
resetDownload(uuid);
|
||||
qWarning() << Q_FUNC_INFO
|
||||
<< QMetaEnum::fromType<QNetworkReply::NetworkError>().valueToKey(error);
|
||||
Q_EMIT errorOccurred(GetError::NETWORK_ERROR);
|
||||
Q_EMIT errorOccurred(translateErrorCode(error));
|
||||
});
|
||||
|
||||
connect(reply, &QNetworkReply::finished, this, [this, uuid, onDoneCallback, reply, file]() {
|
||||
|
|
|
@ -35,7 +35,14 @@ public:
|
|||
explicit NetworkManager(ConnectivityMonitor* cm, QObject* parent = nullptr);
|
||||
virtual ~NetworkManager() = default;
|
||||
|
||||
enum GetError { DISCONNECTED, NETWORK_ERROR, ACCESS_DENIED, SSL_ERROR, CANCELED };
|
||||
enum GetError {
|
||||
DISCONNECTED,
|
||||
CONTENT_NOT_FOUND,
|
||||
ACCESS_DENIED,
|
||||
SSL_ERROR,
|
||||
CANCELED,
|
||||
NETWORK_ERROR,
|
||||
};
|
||||
Q_ENUM(GetError)
|
||||
|
||||
void sendGetRequest(const QUrl& url, std::function<void(const QByteArray&)>&& onDoneCallback);
|
||||
|
@ -48,7 +55,7 @@ public:
|
|||
int replyId,
|
||||
std::function<void(bool, const QString&)>&& onDoneCallback,
|
||||
const QString& filePath,
|
||||
const QString& extension = "");
|
||||
const QString& extension = {});
|
||||
void resetDownload(int replyId);
|
||||
void cancelDownload(int replyId);
|
||||
Q_SIGNALS:
|
||||
|
|
|
@ -36,8 +36,7 @@ SimpleMessageDialog {
|
|||
Connections {
|
||||
target: AppVersionManager
|
||||
|
||||
function onErrorOccurred(error, msg) {
|
||||
console.warn("Error while downloading update: " + error + " - " + msg);
|
||||
function onNetworkErrorOccurred(error) {
|
||||
downloadDialog.close();
|
||||
}
|
||||
|
||||
|
|
|
@ -32,28 +32,6 @@ SettingsPageBase {
|
|||
|
||||
title: JamiStrings.updatesTitle
|
||||
|
||||
function presentInfoDialog(infoText) {
|
||||
viewCoordinator.presentDialog(appWindow, "commoncomponents/SimpleMessageDialog.qml", {
|
||||
"title": JamiStrings.updateDialogTitle,
|
||||
"infoText": infoText,
|
||||
"buttonTitles": [JamiStrings.optionOk],
|
||||
"buttonStyles": [SimpleMessageDialog.ButtonStyle.TintedBlue],
|
||||
"buttonCallBacks": []
|
||||
});
|
||||
}
|
||||
|
||||
function presentConfirmInstallDialog(infoText, beta) {
|
||||
viewCoordinator.presentDialog(appWindow, "commoncomponents/SimpleMessageDialog.qml", {
|
||||
"title": JamiStrings.updateDialogTitle,
|
||||
"infoText": infoText,
|
||||
"buttonTitles": [JamiStrings.optionUpgrade, JamiStrings.optionLater],
|
||||
"buttonStyles": [SimpleMessageDialog.ButtonStyle.TintedBlue, SimpleMessageDialog.ButtonStyle.TintedBlue],
|
||||
"buttonCallBacks": [function () {
|
||||
AppVersionManager.applyUpdates(beta);
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
flickableContent: ColumnLayout {
|
||||
id: manageAccountEnableColumnLayout
|
||||
width: contentFlickableWidth
|
||||
|
@ -119,15 +97,7 @@ SettingsPageBase {
|
|||
toolTipText: JamiStrings.betaInstall
|
||||
text: JamiStrings.betaInstall
|
||||
|
||||
onClicked: viewCoordinator.presentDialog(appWindow, "commoncomponents/SimpleMessageDialog.qml", {
|
||||
"title": JamiStrings.updateDialogTitle,
|
||||
"infoText": JamiStrings.confirmBeta,
|
||||
"buttonTitles": [JamiStrings.optionUpgrade, JamiStrings.optionLater],
|
||||
"buttonStyles": [SimpleMessageDialog.ButtonStyle.TintedBlue, SimpleMessageDialog.ButtonStyle.TintedBlue],
|
||||
"buttonCallBacks": [function () {
|
||||
AppVersionManager.applyUpdates(true);
|
||||
}]
|
||||
})
|
||||
onClicked: appWindow.presentUpdateConfirmInstallDialog(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue