From 98d60facb7a358e0679e25ed661e5c80e4da901a Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Mon, 26 Sep 2022 04:33:21 +0200 Subject: [PATCH] GlosSIConfig: Update notifications --- GlosSIConfig/.clang-format | 2 +- GlosSIConfig/Resource.rc | 8 +- GlosSIConfig/UIModel.cpp | 192 +++++++++++++++++++++++++------------ GlosSIConfig/UIModel.h | 21 +++- GlosSIConfig/qml/main.qml | 12 +++ bundle-zip.ps1 | 2 + 6 files changed, 167 insertions(+), 70 deletions(-) diff --git a/GlosSIConfig/.clang-format b/GlosSIConfig/.clang-format index 7cc5c0d..7d2c5a9 100644 --- a/GlosSIConfig/.clang-format +++ b/GlosSIConfig/.clang-format @@ -4,5 +4,5 @@ IndentWidth: 4 BreakBeforeBraces: "Stroustrup" AllowShortIfStatementsOnASingleLine: false IndentCaseLabels: false -ColumnLimit: 0 +ColumnLimit: 120 PointerAlignment: "Left" \ No newline at end of file diff --git a/GlosSIConfig/Resource.rc b/GlosSIConfig/Resource.rc index 4f2200b..f8b7337 100644 --- a/GlosSIConfig/Resource.rc +++ b/GlosSIConfig/Resource.rc @@ -51,8 +51,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,0,9,1014000053108 - PRODUCTVERSION 0,0,9,1014000053108 + FILEVERSION 0,0,9,1029000008904 + PRODUCTVERSION 0,0,9,1029000008904 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BEGIN VALUE "CompanyName", "Peter Repukat - FlatspotSoftware" VALUE "FileDescription", "GlosSI - Config" - VALUE "FileVersion", "0.0.9.1-14-gda531f8" + VALUE "FileVersion", "0.0.9.1-29-gefe89c4" VALUE "InternalName", "GlosSIConfig" VALUE "LegalCopyright", "Copyright (C) 2021 Peter Repukat - FlatspotSoftware" VALUE "OriginalFilename", "GlosSIConfig.exe" VALUE "ProductName", "GlosSI" - VALUE "ProductVersion", "0.0.9.1-14-gda531f8" + VALUE "ProductVersion", "0.0.9.1-29-gefe89c4" END END BLOCK "VarFileInfo" diff --git a/GlosSIConfig/UIModel.cpp b/GlosSIConfig/UIModel.cpp index 3d1707e..6704918 100644 --- a/GlosSIConfig/UIModel.cpp +++ b/GlosSIConfig/UIModel.cpp @@ -18,9 +18,13 @@ limitations under the License. #include #include #include +#include +#include #include +#include + #ifdef _WIN32 #include "UWPFetch.h" #include @@ -30,10 +34,7 @@ limitations under the License. UIModel::UIModel() : QObject(nullptr) { - auto path = std::filesystem::temp_directory_path() - .parent_path() - .parent_path() - .parent_path(); + auto path = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path(); path /= "Roaming"; path /= "GlosSI"; @@ -50,15 +51,14 @@ UIModel::UIModel() : QObject(nullptr) parseShortcutVDF(); readTargetConfigs(); + updateCheck(); } void UIModel::readTargetConfigs() { QDir dir(config_dir_name_); auto entries = dir.entryList(QDir::Files, QDir::SortFlag::Name); - entries.removeIf([](const auto& entry) { - return !entry.endsWith(".json"); - }); + entries.removeIf([](const auto& entry) { return !entry.endsWith(".json"); }); std::for_each(entries.begin(), entries.end(), [this](const auto& name) { auto path = config_path_; @@ -74,9 +74,8 @@ void UIModel::readTargetConfigs() const auto jsondoc = QJsonDocument::fromJson(data); auto filejson = jsondoc.object(); - filejson["name"] = filejson.contains("name") - ? filejson["name"].toString() - : QString(name).replace(QRegularExpression("\\.json"), ""); + filejson["name"] = filejson.contains("name") ? filejson["name"].toString() + : QString(name).replace(QRegularExpression("\\.json"), ""); targets_.append(filejson.toVariantMap()); }); @@ -84,10 +83,7 @@ void UIModel::readTargetConfigs() emit targetListChanged(); } -QVariantList UIModel::getTargetList() const -{ - return targets_; -} +QVariantList UIModel::getTargetList() const { return targets_; } void UIModel::addTarget(QVariant shortcut) { @@ -103,7 +99,8 @@ void UIModel::updateTarget(int index, QVariant shortcut) const auto map = shortcut.toMap(); const auto json = QJsonObject::fromVariantMap(map); - auto oldName = targets_[index].toMap()["name"].toString().replace(QRegularExpression("[\\\\/:*?\"<>|]"), "") + ".json"; + auto oldName = + targets_[index].toMap()["name"].toString().replace(QRegularExpression("[\\\\/:*?\"<>|]"), "") + ".json"; auto path = config_path_; path /= config_dir_name_.toStdString(); path /= (oldName).toStdString(); @@ -117,7 +114,8 @@ void UIModel::updateTarget(int index, QVariant shortcut) void UIModel::deleteTarget(int index) { - auto oldName = targets_[index].toMap()["name"].toString().replace(QRegularExpression("[\\\\/:*?\"<>|]"), "") + ".json"; + auto oldName = + targets_[index].toMap()["name"].toString().replace(QRegularExpression("[\\\\/:*?\"<>|]"), "") + ".json"; auto path = config_path_; path /= config_dir_name_.toStdString(); path /= (oldName).toStdString(); @@ -151,11 +149,13 @@ bool UIModel::addToSteam(QVariant shortcut, const QString& shortcutspath, bool f VDFParser::Shortcut vdfshortcut; vdfshortcut.appname = name.toStdString(); vdfshortcut.exe = ("\"" + appDir.absolutePath() + "/GlosSITarget.exe" + "\"").toStdString(); - vdfshortcut.StartDir = (launch && !maybeLaunchPath.isEmpty() - ? (std::string("\"") + std::filesystem::path(maybeLaunchPath.toStdString()).parent_path().string() + "\"") - : ("\"" + appDir.absolutePath() + "\"").toStdString()); + vdfshortcut.StartDir = + (launch && !maybeLaunchPath.isEmpty() + ? (std::string("\"") + std::filesystem::path(maybeLaunchPath.toStdString()).parent_path().string() + "\"") + : ("\"" + appDir.absolutePath() + "\"").toStdString()); // ShortcutPath; default - vdfshortcut.LaunchOptions = (QString(name).replace(QRegularExpression("[\\\\/:*?\"<>|]"), "") + ".json").toStdString(); + vdfshortcut.LaunchOptions = + (QString(name).replace(QRegularExpression("[\\\\/:*?\"<>|]"), "") + ".json").toStdString(); // IsHidden; default // AllowDesktopConfig; default // AllowOverlay; default @@ -168,11 +168,16 @@ bool UIModel::addToSteam(QVariant shortcut, const QString& shortcutspath, bool f if (maybeIcon.isEmpty()) { if (launch && !maybeLaunchPath.isEmpty()) vdfshortcut.icon = - "\"" + (is_windows_ ? QString(maybeLaunchPath).replace(QRegularExpression("\\/"), "\\").toStdString() : maybeLaunchPath.toStdString()) + "\""; + "\"" + + (is_windows_ ? QString(maybeLaunchPath).replace(QRegularExpression("\\/"), "\\").toStdString() + : maybeLaunchPath.toStdString()) + + "\""; } else { - vdfshortcut.icon = - "\"" + (is_windows_ ? QString(maybeIcon).replace(QRegularExpression("\\/"), "\\").toStdString() : maybeIcon.toStdString()) + "\""; + vdfshortcut.icon = "\"" + + (is_windows_ ? QString(maybeIcon).replace(QRegularExpression("\\/"), "\\").toStdString() + : maybeIcon.toStdString()) + + "\""; } // Add installed locally and GlosSI tag vdfshortcut.tags.push_back("Installed locally"); @@ -199,10 +204,11 @@ bool UIModel::addToSteam(const QString& name, const QString& shortcutspath, bool bool UIModel::removeFromSteam(const QString& name, const QString& shortcutspath, bool from_cmd) { qDebug() << "trying to remove " << name << " from steam"; - shortcuts_vdf_.erase(std::ranges::remove_if(shortcuts_vdf_, [&name](const auto& shortcut) { - return shortcut.appname == name.toStdString(); - }).begin(), - shortcuts_vdf_.end()); + shortcuts_vdf_.erase( + std::ranges::remove_if(shortcuts_vdf_, + [&name](const auto& shortcut) { return shortcut.appname == name.toStdString(); }) + .begin(), + shortcuts_vdf_.end()); return writeShortcutsVDF(L"remove", name.toStdWString(), shortcutspath.toStdWString(), from_cmd); } @@ -218,17 +224,21 @@ QVariantMap UIModel::manualProps(QVariant shortcut) res.insert("name", name); res.insert("config", name + ".json"); res.insert("launch", ("\"" + appDir.absolutePath() + "/GlosSITarget.exe" + "\"")); - res.insert("launchDir", ( - launch && !maybeLaunchPath.isEmpty() - ? (QString("\"") + QString::fromStdString(std::filesystem::path(maybeLaunchPath.toStdString()).parent_path().string()) + "\"") - : ("\"" + appDir.absolutePath() + "\""))); + res.insert( + "launchDir", + (launch && !maybeLaunchPath.isEmpty() + ? (QString("\"") + + QString::fromStdString(std::filesystem::path(maybeLaunchPath.toStdString()).parent_path().string()) + + "\"") + : ("\"" + appDir.absolutePath() + "\""))); return res; } void UIModel::enableSteamInputXboxSupport() { if (foundSteam()) { - const std::filesystem::path config_path = std::wstring(getSteamPath()) + user_data_path_.toStdWString() + getSteamUserId() + user_config_file_.toStdWString(); + const std::filesystem::path config_path = std::wstring(getSteamPath()) + user_data_path_.toStdWString() + + getSteamUserId() + user_config_file_.toStdWString(); if (!std::filesystem::exists(config_path)) { qDebug() << "localconfig.vdf does not exist."; } @@ -265,19 +275,38 @@ void UIModel::enableSteamInputXboxSupport() } } -#ifdef _WIN32 -QVariantList UIModel::uwpApps() +void UIModel::updateCheck() { - return UWPFetch::UWPAppList(); + auto manager = new QNetworkAccessManager(); + QNetworkRequest request; + QNetworkReply* reply = NULL; + + QSslConfiguration config = QSslConfiguration::defaultConfiguration(); + config.setProtocol(QSsl::TlsV1_2); + request.setSslConfiguration(config); + request.setUrl(QUrl("https://glossi.1-3-3-7.dev/api/availFiles")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + reply = manager->get(request); + // connect( + // manager, &QNetworkAccessManager::finished, this, [](QNetworkReply* rep) { + // qDebug() << rep->readAll(); + // }); + connect(manager, &QNetworkAccessManager::finished, this, &UIModel::onAvailFilesResponse); } + +#ifdef _WIN32 +QVariantList UIModel::uwpApps() { return UWPFetch::UWPAppList(); } #endif -bool UIModel::writeShortcutsVDF(const std::wstring& mode, const std::wstring& name, const std::wstring& shortcutspath, bool is_admin_try) const +bool UIModel::writeShortcutsVDF(const std::wstring& mode, const std::wstring& name, const std::wstring& shortcutspath, + bool is_admin_try) const { #ifdef _WIN32 const std::filesystem::path config_path = is_admin_try ? shortcutspath - : std::wstring(getSteamPath()) + user_data_path_.toStdWString() + getSteamUserId() + shortcutsfile_.toStdWString(); + : std::wstring(getSteamPath()) + user_data_path_.toStdWString() + + getSteamUserId() + shortcutsfile_.toStdWString(); qDebug() << "Steam config Path: " << config_path; qDebug() << "Trying to write config as admin: " << is_admin_try; @@ -331,15 +360,9 @@ bool UIModel::writeShortcutsVDF(const std::wstring& mode, const std::wstring& na #endif } -bool UIModel::getIsWindows() const -{ - return is_windows_; -} +bool UIModel::getIsWindows() const { return is_windows_; } -bool UIModel::hasAcrylicEffect() const -{ - return has_acrylic_affect_; -} +bool UIModel::hasAcrylicEffect() const { return has_acrylic_affect_; } void UIModel::setAcrylicEffect(bool has_acrylic_affect) { @@ -347,6 +370,56 @@ void UIModel::setAcrylicEffect(bool has_acrylic_affect) emit acrylicChanged(); } +void UIModel::onAvailFilesResponse(QNetworkReply* reply) +{ + + const QVariant status_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); + qDebug() << "http status: " << status_code; + if (status_code.isValid() && status_code.toInt() == 200) { + const auto respStr = reply->readAll(); + qDebug() << "AvailFiles response: " << respStr; + + QJsonObject json = QJsonDocument::fromJson(respStr).object(); + + struct VersionInfo { + int major; + int minor; + int patch; + int revision; + int commits_since_last; + }; + + std::vector> new_versions; + for (const auto& info : + json.keys() | std::ranges::views::filter([this, &json](const auto& key) { + return notify_on_snapshots_ ? true : json[key].toObject().value("type") == "release"; + }) | std::ranges::views::transform([&json](const auto& key) -> std::pair { + const auto versionString = json[key].toObject().value("version").toString(); + const auto cleanVersion = versionString.split("-")[0]; + const auto versionSplits = cleanVersion.split("."); + return {key, + {versionSplits[0].toInt(), versionSplits[1].toInt(), versionSplits[2].toInt(), + versionSplits[3].toInt(), + versionString.count('-') == 2 ? versionString.split("-")[1].toInt() : 0}}; + }) | std::views::filter([](const auto& info) { + return info.second.major > version::VERSION_MAJOR || info.second.minor > version::VERSION_MINOR || + info.second.patch > version::VERSION_PATCH || + info.second.revision > version::VERSION_REVISION || + info.second.commits_since_last > (QString(version::VERSION_STR).count('-') == 2 + ? QString(version::VERSION_STR).split("-")[1].toInt() + : 0); + }) | std::ranges::views::all) { + new_versions.push_back(info); + } + std::ranges::sort(new_versions, [](const auto& a, const auto& b) { return a.first > b.first; }); + if (!new_versions.empty()) { + qDebug() << "New version available: " << new_versions[0].first; + new_version_name_ = new_versions[0].first; + emit newVersionAvailable(); + } + } +} + void UIModel::writeTarget(const QJsonObject& json, const QString& name) const { auto path = config_path_; @@ -358,18 +431,13 @@ void UIModel::writeTarget(const QJsonObject& json, const QString& name) const return; } - file.write( - QString(QJsonDocument(json).toJson(QJsonDocument::Indented)) - .toStdString() - .data() - ); + file.write(QString(QJsonDocument(json).toJson(QJsonDocument::Indented)).toStdString().data()); file.close(); } -QString UIModel::getVersionString() const -{ - return QString(version::VERSION_STR); -} +QString UIModel::getVersionString() const { return QString(version::VERSION_STR); } + +QString UIModel::getNewVersionName() const { return new_version_name_; } std::filesystem::path UIModel::getSteamPath() const { @@ -388,7 +456,7 @@ std::filesystem::path UIModel::getSteamPath() const return ""; } #else - return L""; // TODO LINUX + return L""; // TODO LINUX #endif } @@ -407,11 +475,12 @@ std::wstring UIModel::getSteamUserId() const qDebug() << "Steam not open?"; } return res; - } catch(...) { + } + catch (...) { return L"0"; } #else - return L""; // TODO LINUX + return L""; // TODO LINUX #endif } @@ -420,7 +489,8 @@ bool UIModel::foundSteam() const if (getSteamPath() == "" || getSteamUserId() == L"0") { return false; } - const std::filesystem::path user_config_dir = std::wstring(getSteamPath()) + user_data_path_.toStdWString() + getSteamUserId(); + const std::filesystem::path user_config_dir = + std::wstring(getSteamPath()) + user_data_path_.toStdWString() + getSteamUserId(); if (!std::filesystem::exists(user_config_dir)) { return false; } @@ -429,7 +499,8 @@ bool UIModel::foundSteam() const void UIModel::parseShortcutVDF() { - const std::filesystem::path config_path = std::wstring(getSteamPath()) + user_data_path_.toStdWString() + getSteamUserId() + shortcutsfile_.toStdWString(); + const std::filesystem::path config_path = std::wstring(getSteamPath()) + user_data_path_.toStdWString() + + getSteamUserId() + shortcutsfile_.toStdWString(); if (!std::filesystem::exists(config_path)) { qDebug() << "Shortcuts file does not exist."; return; @@ -447,7 +518,8 @@ bool UIModel::isSteamInputXboxSupportEnabled() const { // return true as default to not bug the user in error cases. if (foundSteam()) { - const std::filesystem::path config_path = std::wstring(getSteamPath()) + user_data_path_.toStdWString() + getSteamUserId() + user_config_file_.toStdWString(); + const std::filesystem::path config_path = std::wstring(getSteamPath()) + user_data_path_.toStdWString() + + getSteamUserId() + user_config_file_.toStdWString(); if (!std::filesystem::exists(config_path)) { qDebug() << "localconfig.vdf does not exist."; return true; diff --git a/GlosSIConfig/UIModel.h b/GlosSIConfig/UIModel.h index 5c35259..0daadea 100644 --- a/GlosSIConfig/UIModel.h +++ b/GlosSIConfig/UIModel.h @@ -14,11 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ #pragma once -#include #include #include #include #include +#include + +class QNetworkReply; class UIModel : public QObject { Q_OBJECT @@ -30,8 +32,8 @@ class UIModel : public QObject { Q_PROPERTY(bool foundSteam READ foundSteam CONSTANT) Q_PROPERTY(bool steamInputXboxSupportEnabled READ isSteamInputXboxSupportEnabled CONSTANT) - Q_PROPERTY(QString versionString READ getVersionString CONSTANT) + Q_PROPERTY(QString newVersionName READ getNewVersionName NOTIFY newVersionAvailable) public: UIModel(); @@ -47,6 +49,8 @@ class UIModel : public QObject { Q_INVOKABLE bool removeFromSteam(const QString& name, const QString& shortcutspath, bool from_cmd = false); Q_INVOKABLE QVariantMap manualProps(QVariant shortcut); Q_INVOKABLE void enableSteamInputXboxSupport(); + + Q_INVOKABLE void updateCheck(); #ifdef _WIN32 Q_INVOKABLE QVariantList uwpApps(); #endif @@ -55,8 +59,7 @@ class UIModel : public QObject { const std::wstring& mode, const std::wstring& name, const std::wstring& shortcutspath, - bool is_admin_try = false - ) const; + bool is_admin_try = false) const; bool getIsWindows() const; [[nodiscard]] bool hasAcrylicEffect() const; @@ -65,6 +68,10 @@ class UIModel : public QObject { signals: void acrylicChanged(); void targetListChanged(); + void newVersionAvailable(); + + public slots: + void onAvailFilesResponse(QNetworkReply* reply); private: #ifdef _WIN32 @@ -83,11 +90,15 @@ class UIModel : public QObject { QVariantList targets_; + QString new_version_name_; + bool notify_on_snapshots_ = false; + std::vector shortcuts_vdf_; - + void writeTarget(const QJsonObject& json, const QString& name) const; QString getVersionString() const; + QString getNewVersionName() const; std::filesystem::path getSteamPath() const; std::wstring getSteamUserId() const; diff --git a/GlosSIConfig/qml/main.qml b/GlosSIConfig/qml/main.qml index a643ec5..dcf7be4 100644 --- a/GlosSIConfig/qml/main.qml +++ b/GlosSIConfig/qml/main.qml @@ -82,6 +82,18 @@ Window { } } + InfoDialog { + id: newVersionDialog + titleText: qsTr("New version available!") + text: qsTr("Would you like to visit the download page now?") + onConfirmed: function () { + Qt.openUrlExternally(`https://glossi.flatspot.pictures/#downloads-${uiModel.newVersionName}`); + } + extraButton: true + extraButtonText: qsTr("Remind me later") + visible: !!uiModel.newVersionName + } + Rectangle { id: titleBar visible: uiModel.isWindows diff --git a/bundle-zip.ps1 b/bundle-zip.ps1 index e33b25e..c6964e7 100644 --- a/bundle-zip.ps1 +++ b/bundle-zip.ps1 @@ -18,5 +18,7 @@ Copy-Item "..\..\vc_redist.x64.exe" -Destination "." Copy-Item "..\..\LICENSE" -Destination "./LICENSE" Copy-Item "..\..\QT_License" -Destination "./QT_License" Copy-Item "..\..\THIRD_PARTY_LICENSES.txt" -Destination "./THIRD_PARTY_LICENSES.txt" +Copy-Item "C:\Qt\Tools\OpenSSL\Win_x64\bin\libcrypto-1_1-x64.dll" -Destination "./libcrypto-1_1-x64.dll" +Copy-Item "C:\Qt\Tools\OpenSSL\Win_x64\bin\libssl-1_1-x64.dll" -Destination "./libssl-1_1-x64.dll" 7z a GlosSI-snapshot.zip * \ No newline at end of file