Skip to content

Commit

Permalink
#3 Add simple updater
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexandrePTJ committed May 13, 2020
1 parent dc3c737 commit 45326c2
Show file tree
Hide file tree
Showing 13 changed files with 240 additions and 8 deletions.
11 changes: 9 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
0.2.0 (Unreleased)
==================

- [#6](https://github.com/AlexandrePTJ/kemai/issues/6) Handle user timezone correctly
- [#3](https://github.com/AlexandrePTJ/kemai/issues/3) Add updater


0.1.1 (2020-05-09)
==================

- #2 Fix minimal OSX version
- [#2](https://github.com/AlexandrePTJ/kemai/issues/2) Fix minimal OSX version


0.1.0 (2020-05-04)
==================

Initial version
Initial version
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ set(CMAKE_AUTORCC ON)

#
add_subdirectory(client)
add_subdirectory(updater)
add_subdirectory(app)
3 changes: 2 additions & 1 deletion src/app/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,5 @@ elseif(APPLE)
endif()

target_sources(${PROJECT_NAME} PRIVATE ${SRCS} ${HDRS} ${UIS} ${RESX} ${KEMAI_ICNS})
target_link_libraries(${PROJECT_NAME} Qt5::Widgets KemaiClient)
target_include_directories(${PROJECT_NAME} PRIVATE ${PROJECT_BINARY_DIR})
target_link_libraries(${PROJECT_NAME} Qt5::Widgets KemaiClient KemaiUpdater)
2 changes: 1 addition & 1 deletion src/app/kemai_version.h.in
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#pragma once

#define KEMAI_VERSION "@Kemai_VERSION@"
#define KEMAI_VERSION "@KemaiProject_VERSION@"
58 changes: 57 additions & 1 deletion src/app/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "ui_mainwindow.h"

#include "helpers.h"
#include "kemai_version.h"
#include "settings.h"

#include "activitywidget.h"
Expand All @@ -15,10 +16,13 @@

#include <QCloseEvent>
#include <QDebug>
#include <QDesktopServices>
#include <QMessageBox>
#include <QTimer>

using namespace kemai::app;
using namespace kemai::client;
using namespace kemai::updater;

MainWindow::MainWindow() : QMainWindow(), mUi(new Ui::MainWindow)
{
Expand All @@ -38,6 +42,7 @@ MainWindow::MainWindow() : QMainWindow(), mUi(new Ui::MainWindow)
mActNewCustomer = new QAction(tr("New customer..."), this);
mActNewProject = new QAction(tr("New project..."), this);
mActNewActivity = new QAction(tr("New activity..."), this);
mActCheckUpdate = new QAction(tr("Check for updates..."), this);

/*
* Setup systemtray
Expand Down Expand Up @@ -67,8 +72,14 @@ MainWindow::MainWindow() : QMainWindow(), mUi(new Ui::MainWindow)
editMenu->addAction(mActNewProject);
editMenu->addAction(mActNewActivity);

auto helpMenu = new QMenu(tr("&Help"), mMenuBar);
helpMenu->addAction(mActCheckUpdate);
helpMenu->addSeparator();
helpMenu->addAction(tr("About Qt"), qApp, &QApplication::aboutQt);

mMenuBar->addMenu(fileMenu);
mMenuBar->addMenu(editMenu);
mMenuBar->addMenu(helpMenu);
setMenuBar(mMenuBar);

/*
Expand All @@ -91,12 +102,19 @@ MainWindow::MainWindow() : QMainWindow(), mUi(new Ui::MainWindow)
connect(mActNewCustomer, &QAction::triggered, this, &MainWindow::onActionNewCustomerTriggered);
connect(mActNewProject, &QAction::triggered, this, &MainWindow::onActionNewProjectTriggered);
connect(mActNewActivity, &QAction::triggered, this, &MainWindow::onActionNewActivityTriggered);
connect(mActCheckUpdate, &QAction::triggered, this, &MainWindow::onActionCheckUpdateTriggered);
connect(mSystemTrayIcon, &QSystemTrayIcon::activated, this, &MainWindow::onSystemTrayActivated);
connect(&mUpdater, &KemaiUpdater::checkFinished, this, &MainWindow::onNewVersionCheckFinished);

/*
* Delay first refresh
* Delay first refresh and update check
*/
QTimer::singleShot(100, activityWidget, &ActivityWidget::refresh);
QTimer::singleShot(100, [&]() {
auto ignoreVersion = QVersionNumber::fromString(Settings::load().ignoredVersion);
auto currentVersion = QVersionNumber::fromString(KEMAI_VERSION);
mUpdater.checkAvailableNewVersion(currentVersion >= ignoreVersion ? currentVersion : ignoreVersion, true);
});

// Get client
refreshClient();
Expand Down Expand Up @@ -180,6 +198,12 @@ void MainWindow::onActionNewActivityTriggered()
}
}

void MainWindow::onActionCheckUpdateTriggered()
{
auto currentVersion = QVersionNumber::fromString(KEMAI_VERSION);
mUpdater.checkAvailableNewVersion(currentVersion, false);
}

void MainWindow::onStackedCurrentChanged(int id)
{
// Check if we left settings stack
Expand Down Expand Up @@ -211,3 +235,35 @@ void MainWindow::onSystemTrayActivated(QSystemTrayIcon::ActivationReason reason)
break;
}
}

void MainWindow::onNewVersionCheckFinished(const VersionDetails& details)
{
if (not details.vn.isNull())
{
auto res = QMessageBox::information(
this, tr("New version available"),
tr("Version %1 is available.\n\n%2").arg(details.vn.toString()).arg(details.description),
QMessageBox::Open | QMessageBox::Ignore | QMessageBox::Cancel, QMessageBox::Open);

switch (res)
{
case QMessageBox::Open:
QDesktopServices::openUrl(details.url);
break;

case QMessageBox::Ignore: {
auto settings = Settings::load();
settings.ignoredVersion = details.vn.toString();
Settings::save(settings);
}
break;

default:
break;
}
}
else
{
QMessageBox::information(this, tr("No update"), tr("%1 is latest version.").arg(KEMAI_VERSION));
}
}
5 changes: 5 additions & 0 deletions src/app/mainwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <QSystemTrayIcon>

#include "kemai/kimaiclient.h"
#include "kemai/kemaiupdater.h"

namespace Ui {
class MainWindow;
Expand All @@ -31,12 +32,15 @@ private slots:
void onActionNewCustomerTriggered();
void onActionNewProjectTriggered();
void onActionNewActivityTriggered();
void onActionCheckUpdateTriggered();
void onStackedCurrentChanged(int id);
void onSystemTrayActivated(QSystemTrayIcon::ActivationReason reason);
void onNewVersionCheckFinished(const updater::VersionDetails& details);

private:
Ui::MainWindow* mUi;
QSharedPointer<client::KimaiClient> mClient;
updater::KemaiUpdater mUpdater;

// keep stacked widgets ids
int mActivitySId;
Expand All @@ -49,6 +53,7 @@ private slots:
QAction* mActNewCustomer = nullptr;
QAction* mActNewProject = nullptr;
QAction* mActNewActivity = nullptr;
QAction* mActCheckUpdate = nullptr;

// Main menu
QMenuBar* mMenuBar = nullptr;
Expand Down
2 changes: 2 additions & 0 deletions src/app/settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Settings Settings::load(const QString& confPath)
settings.username = qset.value("username").toString();
settings.token = qset.value("token").toString();
settings.closeToSystemTray = qset.value("closeToSystemTray", false).toBool();
settings.ignoredVersion = qset.value("ignoredVersion", "0.0.0").toString();

return settings;
}
Expand All @@ -43,4 +44,5 @@ void Settings::save(const Settings& settings, const QString& confPath)
qset.setValue("username", settings.username);
qset.setValue("token", settings.token);
qset.setValue("closeToSystemTray", settings.closeToSystemTray);
qset.setValue("ignoredVersion", settings.ignoredVersion);
}
2 changes: 2 additions & 0 deletions src/app/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ struct Settings
QString token;
bool closeToSystemTray;

QString ignoredVersion;

bool isReady() const;

static Settings load(const QString& confPath = "");
Expand Down
3 changes: 0 additions & 3 deletions src/client/kimaiclient.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
#include "kemai/kimaiclient.h"
#include "kimaiclient_p.h"

#include <QDebug>
#include <QNetworkRequest>

using namespace kemai::client;

/*
Expand Down
18 changes: 18 additions & 0 deletions src/updater/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
project(KemaiUpdater)

find_package(Qt5 COMPONENTS Network CONFIG REQUIRED)

add_library(${PROJECT_NAME})

set(SRCS
kemaiupdater.cpp)

set(PUB_HDRS
include/kemai/kemaiupdater.h)

set(PRIV_HDRS
kemaiupdater_p.h)

target_sources(${PROJECT_NAME} PRIVATE ${SRCS} ${PRIV_HDRS} ${PUB_HDRS})
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(${PROJECT_NAME} Qt5::Network)
35 changes: 35 additions & 0 deletions src/updater/include/kemai/kemaiupdater.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma once

#include <QObject>
#include <QUrl>
#include <QVersionNumber>

namespace kemai::updater {

struct VersionDetails
{
QVersionNumber vn;
QString description;
QUrl url;
};

class KemaiUpdater : public QObject
{
Q_OBJECT

public:
explicit KemaiUpdater(QObject* parent = nullptr);
virtual ~KemaiUpdater();

void checkAvailableNewVersion(const QVersionNumber& sinceVersion = QVersionNumber(0, 0, 0),
bool silenceIfNoNew = false);

signals:
void checkFinished(const VersionDetails& kv);

private:
class KemaiUpdaterPrivate;
QScopedPointer<KemaiUpdaterPrivate> mD;
};

} // namespace kemai::updater
74 changes: 74 additions & 0 deletions src/updater/kemaiupdater.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#include "kemai/kemaiupdater.h"
#include "kemaiupdater_p.h"

#include <QJsonObject>
#include <QJsonDocument>

using namespace kemai::updater;

/*
* Private impl
*/
KemaiUpdater::KemaiUpdaterPrivate::KemaiUpdaterPrivate(KemaiUpdater* c)
: networkAccessManager(new QNetworkAccessManager), mQ(c)
{
connect(networkAccessManager.data(), &QNetworkAccessManager::finished, this, &KemaiUpdaterPrivate::onNamFinished);
}

QNetworkRequest KemaiUpdater::KemaiUpdaterPrivate::prepareGithubRequest(const QString& path)
{
QUrl url;
url.setScheme("https");
url.setHost("api.github.com");
url.setPath(path);

QNetworkRequest r;
r.setUrl(url);
r.setRawHeader("accept", "application/vnd.github.v3+json");
return r;
}

void KemaiUpdater::KemaiUpdaterPrivate::onNamFinished(QNetworkReply* reply)
{
if (reply->error() != QNetworkReply::NoError)
{
qDebug() << "Error on update check:" << reply->errorString();
}
else
{
// Parse json
auto jdoc = QJsonDocument::fromJson(reply->readAll());
auto jobj = jdoc.object();

auto newVersion = QVersionNumber::fromString(jobj.value("tag_name").toString());
if (newVersion > currentChecksinceVersion)
{
VersionDetails vd;
vd.vn = newVersion;
vd.description = jobj.value("body").toString();
vd.url = jobj.value("html_url").toString();

emit mQ->checkFinished(vd);
}
else if (not silenceIfNoNew)
{
emit mQ->checkFinished(VersionDetails());
}
}
}

/*
* Public impl
*/
KemaiUpdater::KemaiUpdater(QObject* parent) : QObject(parent), mD(new KemaiUpdaterPrivate(this)) {}

KemaiUpdater::~KemaiUpdater() {}

void KemaiUpdater::checkAvailableNewVersion(const QVersionNumber& sinceVersion, bool silenceIfNoNew)
{
mD->currentChecksinceVersion = sinceVersion;
mD->silenceIfNoNew = silenceIfNoNew;

auto rq = mD->prepareGithubRequest("/repos/AlexandrePTJ/kemai/releases/latest");
mD->networkAccessManager->get(rq);
}
34 changes: 34 additions & 0 deletions src/updater/kemaiupdater_p.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#pragma once

//#include <QMap>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QObject>

#include "kemai/kemaiupdater.h"

namespace kemai::updater {

class KemaiUpdater::KemaiUpdaterPrivate : public QObject
{
Q_OBJECT

public:
explicit KemaiUpdaterPrivate(KemaiUpdater* c);

QNetworkRequest prepareGithubRequest(const QString& path);

public:
QVersionNumber currentChecksinceVersion;
bool silenceIfNoNew;

QScopedPointer<QNetworkAccessManager> networkAccessManager;

private slots:
void onNamFinished(QNetworkReply* reply);

private:
KemaiUpdater* const mQ;
};

} // namespace kemai::client

0 comments on commit 45326c2

Please sign in to comment.