From 34afc2731db55ea8c3db4a95290e7159100c19b1 Mon Sep 17 00:00:00 2001 From: Le Tan Date: Thu, 22 Jun 2017 23:12:54 +0800 Subject: [PATCH] help-menu: add update check --- src/dialog/vupdater.cpp | 164 ++++++++++++++++++++++++++++++++++++++++ src/dialog/vupdater.h | 41 ++++++++++ src/src.pro | 6 +- src/utils/vutils.cpp | 14 +++- src/vmainwindow.cpp | 36 ++++++++- 5 files changed, 252 insertions(+), 9 deletions(-) create mode 100644 src/dialog/vupdater.cpp create mode 100644 src/dialog/vupdater.h diff --git a/src/dialog/vupdater.cpp b/src/dialog/vupdater.cpp new file mode 100644 index 00000000..562cd9d5 --- /dev/null +++ b/src/dialog/vupdater.cpp @@ -0,0 +1,164 @@ +#include "vupdater.h" + +#include +#include +#include +#include + +#include "vconfigmanager.h" +#include "vdownloader.h" + +extern VConfigManager vconfig; + +VUpdater::VUpdater(QWidget *p_parent) + : QDialog(p_parent) +{ + setupUI(); +} + +void VUpdater::setupUI() +{ + QImage img(":/resources/icons/vnote.svg"); + QSize imgSize(128, 128); + QLabel *imgLabel = new QLabel(); + imgLabel->setPixmap(QPixmap::fromImage(img.scaled(imgSize))); + + m_versionLabel = new QLabel(tr("Current Version: v%1") + .arg(vconfig.c_version)); + + m_proLabel = new QLabel(tr("Checking for updates...")); + m_proLabel->setOpenExternalLinks(true); + m_proBar = new QProgressBar(); + m_proBar->setTextVisible(false); + + m_descriptionTb = new QTextBrowser(); + + m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok); + connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + + QVBoxLayout *verLayout = new QVBoxLayout(); + verLayout->addStretch(); + verLayout->addWidget(m_versionLabel); + verLayout->addStretch(); + verLayout->addWidget(m_proLabel); + verLayout->addWidget(m_proBar); + + QHBoxLayout *topLayout = new QHBoxLayout(); + topLayout->addWidget(imgLabel); + topLayout->addLayout(verLayout); + + QVBoxLayout *mainLayout = new QVBoxLayout(this); + mainLayout->addLayout(topLayout); + mainLayout->addWidget(m_descriptionTb, 1); + mainLayout->addWidget(m_btnBox); + + m_proLabel->hide(); + m_proBar->hide(); + m_descriptionTb->hide(); + + setLayout(mainLayout); + mainLayout->setSizeConstraint(QLayout::SetFixedSize); + setWindowTitle(tr("VNote Update")); +} + +void VUpdater::showEvent(QShowEvent *p_event) +{ + Q_UNUSED(p_event); + QDialog::showEvent(p_event); + + QTimer *timer = new QTimer(this); + timer->setSingleShot(true); + timer->setInterval(1000); + connect(timer, &QTimer::timeout, + this, [this]() { + this->checkUpdates(); + }); + + timer->start(); +} + +void VUpdater::checkUpdates() +{ + // Change UI. + m_proLabel->setText(tr("Checking for updates...")); + m_proLabel->show(); + + m_proBar->setEnabled(true); + m_proBar->setMinimum(0); + m_proBar->setMaximum(100); + m_proBar->reset(); + m_proBar->show(); + + QString url("https://api.github.com/repos/tamlok/vnote/releases/latest"); + VDownloader *downloader = new VDownloader(this); + connect(downloader, &VDownloader::downloadFinished, + this, &VUpdater::parseResult); + downloader->download(url); + + m_proBar->setValue(20); +} + +// Return if @p_latestVersion is newer than p_curVersion. +// They are both in format xx.xx +bool isNewerVersion(const QString &p_curVersion, const QString &p_latestVersion) +{ + QStringList curList = p_curVersion.split('.', QString::SkipEmptyParts); + QStringList latestList = p_latestVersion.split('.', QString::SkipEmptyParts); + + if (curList.size() != 2 || latestList.size() != 2) { + return false; + } + + int a = curList[0].toInt(); + int b = curList[1].toInt(); + int c = latestList[0].toInt(); + int d = latestList[1].toInt(); + + if (a > c) { + return false; + } else if (a < c) { + return true; + } else { + return d > b; + } +} + +void VUpdater::parseResult(const QByteArray &p_data) +{ + m_proBar->setValue(40); + QJsonDocument jsonDoc = QJsonDocument::fromJson(p_data); + QJsonObject json = jsonDoc.object(); + + if (jsonDoc.isNull() || json.empty()) { + m_proBar->setEnabled(false); + m_proLabel->setText(tr(":( Fail to check for updates.\n" + "Please try it later.")); + + return; + } + + m_proBar->setValue(100); + + QString tag = json["tag_name"].toString(); + if (tag.startsWith('v') && tag.size() > 3) { + tag = tag.right(tag.size() - 1); + } + + QString releaseName = json["name"].toString(); + QString releaseUrl = json["html_url"].toString(); + QString body = json["body"].toString(); + + m_versionLabel->setText(tr("Current Version: v%1\nLatest Version: v%2") + .arg(vconfig.c_version).arg(tag)); + if (isNewerVersion(vconfig.c_version, tag)) { + m_proLabel->setText(tr("Updates Available!
" + "Please visit Github Releases to download the latest version.") + .arg(releaseUrl)); + } else { + m_proLabel->setText(tr("VNote is already the latest version.")); + } + + m_descriptionTb->setText(releaseName + "\n==========\n" + body); + m_descriptionTb->show(); + m_proBar->hide(); +} diff --git a/src/dialog/vupdater.h b/src/dialog/vupdater.h new file mode 100644 index 00000000..d6d4a816 --- /dev/null +++ b/src/dialog/vupdater.h @@ -0,0 +1,41 @@ +#ifndef VUPDATER_H +#define VUPDATER_H + +#include +#include + +class QLabel; +class QDialogButtonBox; +class QTextBrowser; +class QProgressBar; +class QShowEvent; + +class VUpdater : public QDialog +{ + Q_OBJECT +public: + VUpdater(QWidget *p_parent = 0); + +protected: + void showEvent(QShowEvent *p_event) Q_DECL_OVERRIDE; + +private slots: + // Calling to Github api got responses. + void parseResult(const QByteArray &p_data); + +private: + void setupUI(); + + // Fetch the latest release info from Github. + void checkUpdates(); + + QLabel *m_versionLabel; + QTextBrowser *m_descriptionTb; + QDialogButtonBox *m_btnBox; + + // Progress label and bar. + QLabel *m_proLabel; + QProgressBar *m_proBar; +}; + +#endif // VUPDATER_H diff --git a/src/src.pro b/src/src.pro index 1b54ac8a..3ac35340 100644 --- a/src/src.pro +++ b/src/src.pro @@ -69,7 +69,8 @@ SOURCES += main.cpp\ utils/veditutils.cpp \ vvimindicator.cpp \ vbuttonwithwidget.cpp \ - vtabindicator.cpp + vtabindicator.cpp \ + dialog/vupdater.cpp HEADERS += vmainwindow.h \ vdirectorytree.h \ @@ -126,7 +127,8 @@ HEADERS += vmainwindow.h \ vvimindicator.h \ vbuttonwithwidget.h \ vedittabinfo.h \ - vtabindicator.h + vtabindicator.h \ + dialog/vupdater.h RESOURCES += \ vnote.qrc \ diff --git a/src/utils/vutils.cpp b/src/utils/vutils.cpp index 806c6d31..3b7a7acc 100644 --- a/src/utils/vutils.cpp +++ b/src/utils/vutils.cpp @@ -469,9 +469,17 @@ DocType VUtils::docTypeFromName(const QString &p_name) static QMap> suffixes; if (suffixes.isEmpty()) { - suffixes[(int)DocType::Markdown] = {"md", "markdown", "mkd"}; - suffixes[(int)DocType::List] = {"ls", "list"}; - suffixes[(int)DocType::Container] = {"co", "container", "con"}; + QVector md; + md << "md" << "markdown" << "mkd"; + suffixes[(int)DocType::Markdown] = md; + + QVector list; + list << "ls" << "list"; + suffixes[(int)DocType::List] = list; + + QVector container; + container << "co" << "container" << "con"; + suffixes[(int)DocType::Container] = container; } QString suf = QFileInfo(p_name).suffix().toLower(); diff --git a/src/vmainwindow.cpp b/src/vmainwindow.cpp index 9acb5768..b046bf13 100644 --- a/src/vmainwindow.cpp +++ b/src/vmainwindow.cpp @@ -22,6 +22,7 @@ #include "vmdtab.h" #include "vvimindicator.h" #include "vtabindicator.h" +#include "dialog/vupdater.h" extern VConfigManager vconfig; @@ -320,6 +321,30 @@ void VMainWindow::initHelpMenu() connect(shortcutAct, &QAction::triggered, this, &VMainWindow::shortcutHelp); + QAction *updateAct = new QAction(tr("Check For &Updates"), this); + updateAct->setToolTip(tr("Check for updates of VNote")); + connect(updateAct, &QAction::triggered, + this, [this]() { + VUpdater updater(this); + updater.exec(); + }); + + QAction *starAct = new QAction(tr("Star VNote on &Github"), this); + starAct->setToolTip(tr("Give a star to VNote on Github project")); + connect(starAct, &QAction::triggered, + this, [this]() { + QString url("https://github.com/tamlok/vnote"); + QDesktopServices::openUrl(url); + }); + + QAction *feedbackAct = new QAction(tr("&Feedback"), this); + feedbackAct->setToolTip(tr("Open an issue on Github")); + connect(feedbackAct, &QAction::triggered, + this, [this]() { + QString url("https://github.com/tamlok/vnote/issues"); + QDesktopServices::openUrl(url); + }); + QAction *aboutAct = new QAction(tr("&About VNote"), this); aboutAct->setToolTip(tr("View information about VNote")); connect(aboutAct, &QAction::triggered, @@ -335,6 +360,9 @@ void VMainWindow::initHelpMenu() #endif helpMenu->addAction(shortcutAct); + helpMenu->addAction(updateAct); + helpMenu->addAction(starAct); + helpMenu->addAction(feedbackAct); helpMenu->addAction(aboutQtAct); helpMenu->addAction(aboutAct); } @@ -797,11 +825,11 @@ void VMainWindow::changeMarkdownConverter(QAction *action) void VMainWindow::aboutMessage() { - QString info = tr("v%1").arg(VConfigManager::c_version); - info += "\n\n"; + QString info = tr("v%1").arg(VConfigManager::c_version); + info += "

"; info += tr("VNote is a Vim-inspired note-taking application for Markdown."); - info += "\n"; - info += tr("Visit https://github.com/tamlok/vnote.git for more information."); + info += "
"; + info += tr("Please visit Github for more information."); QMessageBox::about(this, tr("About VNote"), info); }