feat: Automatic update subscription

This commit is contained in:
arm64v8a
2023-07-24 13:58:28 +09:00
parent 42b4367f01
commit dad2013a00
21 changed files with 300 additions and 141 deletions

View File

@@ -1061,7 +1061,6 @@ namespace NekoGui {
auto configFn = ":/neko/vpn/sing-box-vpn.json"; auto configFn = ":/neko/vpn/sing-box-vpn.json";
if (QFile::exists("vpn/sing-box-vpn.json")) configFn = "vpn/sing-box-vpn.json"; if (QFile::exists("vpn/sing-box-vpn.json")) configFn = "vpn/sing-box-vpn.json";
auto config = ReadFileText(configFn) auto config = ReadFileText(configFn)
.replace("%ENABLED_FAKEDNS%", dataStore->fake_dns ? "true" : "false")
.replace("//%IPV6_ADDRESS%", dataStore->vpn_ipv6 ? R"("inet6_address": "fdfe:dcba:9876::1/126",)" : "") .replace("//%IPV6_ADDRESS%", dataStore->vpn_ipv6 ? R"("inet6_address": "fdfe:dcba:9876::1/126",)" : "")
.replace("//%SOCKS_USER_PASS%", socks_user_pass) .replace("//%SOCKS_USER_PASS%", socks_user_pass)
.replace("//%PROCESS_NAME_RULE%", process_name_rule) .replace("//%PROCESS_NAME_RULE%", process_name_rule)
@@ -1072,6 +1071,7 @@ namespace NekoGui {
.replace("%STRICT_ROUTE%", dataStore->vpn_strict_route ? "true" : "false") .replace("%STRICT_ROUTE%", dataStore->vpn_strict_route ? "true" : "false")
.replace("%FINAL_OUT%", no_match_out) .replace("%FINAL_OUT%", no_match_out)
.replace("%DNS_ADDRESS%", BOX_UNDERLYING_DNS) .replace("%DNS_ADDRESS%", BOX_UNDERLYING_DNS)
.replace("%FAKE_DNS_ENABLE%", dataStore->fake_dns ? "true" : "false")
.replace("%FAKE_DNS_INBOUND%", dataStore->fake_dns ? "tun-in" : "empty") .replace("%FAKE_DNS_INBOUND%", dataStore->fake_dns ? "tun-in" : "empty")
.replace("%PORT%", Int2String(dataStore->inbound_socks_port)); .replace("%PORT%", Int2String(dataStore->inbound_socks_port));
// hook.js // hook.js

View File

@@ -305,6 +305,7 @@ namespace NekoGui {
_add(new configItem("id", &id, itemType::integer)); _add(new configItem("id", &id, itemType::integer));
_add(new configItem("front_proxy_id", &front_proxy_id, itemType::integer)); _add(new configItem("front_proxy_id", &front_proxy_id, itemType::integer));
_add(new configItem("archive", &archive, itemType::boolean)); _add(new configItem("archive", &archive, itemType::boolean));
_add(new configItem("skip_auto_update", &skip_auto_update, itemType::boolean));
_add(new configItem("name", &name, itemType::string)); _add(new configItem("name", &name, itemType::string));
_add(new configItem("order", &order, itemType::integerList)); _add(new configItem("order", &order, itemType::integerList));
_add(new configItem("url", &url, itemType::string)); _add(new configItem("url", &url, itemType::string));

View File

@@ -8,6 +8,7 @@ namespace NekoGui {
public: public:
int id = -1; int id = -1;
bool archive = false; bool archive = false;
bool skip_auto_update = false;
QString name = ""; QString name = "";
QString url = ""; QString url = "";
QString info = ""; QString info = "";

View File

@@ -4,6 +4,7 @@
#include <QNetworkProxy> #include <QNetworkProxy>
#include <QEventLoop> #include <QEventLoop>
#include <QMetaEnum> #include <QMetaEnum>
#include <QTimer>
#include "main/NekoGui.hpp" #include "main/NekoGui.hpp"
@@ -52,12 +53,21 @@ namespace NekoGui_network {
} }
MW_show_log(QString("SSL Errors: %1 %2").arg(error_str.join(","), NekoGui::dataStore->sub_insecure ? "(Ignored)" : "")); MW_show_log(QString("SSL Errors: %1 %2").arg(error_str.join(","), NekoGui::dataStore->sub_insecure ? "(Ignored)" : ""));
}); });
// // Wait for response
auto abortTimer = new QTimer;
abortTimer->setSingleShot(true);
abortTimer->setInterval(10000);
QObject::connect(abortTimer, &QTimer::timeout, _reply, &QNetworkReply::abort);
abortTimer->start();
{ {
QEventLoop loop; QEventLoop loop;
QObject::connect(&accessManager, &QNetworkAccessManager::finished, &loop, &QEventLoop::quit); QObject::connect(_reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec(); loop.exec();
} }
if (abortTimer != nullptr) {
abortTimer->stop();
abortTimer->deleteLater();
}
// //
auto result = NekoHTTPResponse{_reply->error() == QNetworkReply::NetworkError::NoError ? "" : _reply->errorString(), auto result = NekoHTTPResponse{_reply->error() == QNetworkReply::NetworkError::NoError ? "" : _reply->errorString(),
_reply->readAll(), _reply->rawHeaderPairs()}; _reply->readAll(), _reply->rawHeaderPairs()};

View File

@@ -271,6 +271,7 @@ namespace NekoGui {
_add(new configItem("sp_format", &system_proxy_format, itemType::string)); _add(new configItem("sp_format", &system_proxy_format, itemType::string));
_add(new configItem("sub_clear", &sub_clear, itemType::boolean)); _add(new configItem("sub_clear", &sub_clear, itemType::boolean));
_add(new configItem("sub_insecure", &sub_insecure, itemType::boolean)); _add(new configItem("sub_insecure", &sub_insecure, itemType::boolean));
_add(new configItem("sub_auto_update", &sub_auto_update, itemType::integer));
_add(new configItem("enable_js_hook", &enable_js_hook, itemType::integer)); _add(new configItem("enable_js_hook", &enable_js_hook, itemType::integer));
_add(new configItem("log_ignore", &log_ignore, itemType::stringList)); _add(new configItem("log_ignore", &log_ignore, itemType::stringList));
_add(new configItem("start_minimal", &start_minimal, itemType::boolean)); _add(new configItem("start_minimal", &start_minimal, itemType::boolean));

View File

@@ -121,6 +121,7 @@ namespace NekoGui {
bool sub_use_proxy = false; bool sub_use_proxy = false;
bool sub_clear = false; bool sub_clear = false;
bool sub_insecure = false; bool sub_insecure = false;
int sub_auto_update = -30;
// Security // Security
bool skip_cert = false; bool skip_cert = false;

View File

@@ -28,6 +28,12 @@ inline std::function<void(QString, QString)> MW_dialog_message;
class QThread; class QThread;
inline QThread *DS_cores; inline QThread *DS_cores;
// Timers
class QTimer;
inline QTimer *TM_auto_update_subsctiption;
inline std::function<void(int)> TM_auto_update_subsctiption_Reset_Minute;
// String // String
#define FIRST_OR_SECOND(a, b) a.isEmpty() ? b : a #define FIRST_OR_SECOND(a, b) a.isEmpty() ? b : a

View File

@@ -4,7 +4,7 @@
}, },
"dns": { "dns": {
"fakeip": { "fakeip": {
"enabled": %ENABLED_FAKEDNS%, "enabled": %FAKE_DNS_ENABLE%,
"inet4_range": "198.18.0.0/15", "inet4_range": "198.18.0.0/15",
"inet6_range": "fc00::/18" "inet6_range": "fc00::/18"
}, },

View File

@@ -114,9 +114,7 @@ namespace QtGrpc {
abortTimer = new QTimer; abortTimer = new QTimer;
abortTimer->setSingleShot(true); abortTimer->setSingleShot(true);
abortTimer->setInterval(timeout_ms); abortTimer->setInterval(timeout_ms);
QObject::connect(abortTimer, &QTimer::timeout, abortTimer, [=]() { QObject::connect(abortTimer, &QTimer::timeout, networkReply, &QNetworkReply::abort);
networkReply->abort();
});
abortTimer->start(); abortTimer->start();
} }

View File

@@ -561,3 +561,53 @@ namespace NekoGui_sub {
} }
} }
} // namespace NekoGui_sub } // namespace NekoGui_sub
bool UI_update_all_groups_Updating = false;
#define should_skip_group(g) (g == nullptr || g->url.isEmpty() || g->archive || (onlyAllowed && g->skip_auto_update))
void serialUpdateSubscription(const QList<int> &groupsTabOrder, int _order, bool onlyAllowed) {
// calculate next group
int nextOrder = _order;
std::shared_ptr<NekoGui::Group> nextGroup;
forever {
nextOrder += 1;
if (nextOrder >= groupsTabOrder.size()) break;
auto nextGid = groupsTabOrder[nextOrder];
nextGroup = NekoGui::profileManager->GetGroup(nextGid);
if (should_skip_group(nextGroup)) continue;
break;
}
// calculate this group
auto group = NekoGui::profileManager->GetGroup(groupsTabOrder[_order]);
if (group == nullptr) {
UI_update_all_groups_Updating = false;
return;
}
// v2.2: listener is moved to GroupItem, no refresh here.
NekoGui_sub::groupUpdater->AsyncUpdate(group->url, group->id, [=] {
if (nextGroup == nullptr) {
UI_update_all_groups_Updating = false;
} else {
serialUpdateSubscription(groupsTabOrder, nextOrder, onlyAllowed);
}
});
}
void UI_update_all_groups(bool onlyAllowed) {
if (UI_update_all_groups_Updating) {
MW_show_log("The last subscription update has not exited.");
return;
}
// first: freeze group order
auto groupsTabOrder = NekoGui::profileManager->groupsTabOrder;
for (const auto &gid: groupsTabOrder) {
auto group = NekoGui::profileManager->GetGroup(gid);
if (should_skip_group(group)) continue;
// start
UI_update_all_groups_Updating = true;
serialUpdateSubscription(groupsTabOrder, groupsTabOrder.indexOf(gid), onlyAllowed);
}
}

View File

@@ -29,3 +29,6 @@ namespace NekoGui_sub {
extern GroupUpdater *groupUpdater; extern GroupUpdater *groupUpdater;
} // namespace NekoGui_sub } // namespace NekoGui_sub
// 更新所有订阅 关闭分组窗口时 更新动作继续执行
void UI_update_all_groups(bool onlyAllowed = false);

View File

@@ -247,6 +247,14 @@ For NekoBox, this rewrites the underlying(localhost) DNS in Tun Mode, normal mod
<source>Timeout (s)</source> <source>Timeout (s)</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Automatic update</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Interval (minute, invalid if less than 30)</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>DialogEditGroup</name> <name>DialogEditGroup</name>
@@ -314,6 +322,18 @@ For NekoBox, this rewrites the underlying(localhost) DNS in Tun Mode, normal mod
<source>Clear</source> <source>Clear</source>
<translation type="unfinished">پاک کردن</translation> <translation type="unfinished">پاک کردن</translation>
</message> </message>
<message>
<source>Skip automatic update</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Common</source>
<translation type="unfinished">متداول</translation>
</message>
<message>
<source>Share</source>
<translation type="unfinished">اشتراک گذاری</translation>
</message>
</context> </context>
<context> <context>
<name>DialogEditProfile</name> <name>DialogEditProfile</name>

View File

@@ -243,6 +243,14 @@ For NekoBox, this rewrites the underlying(localhost) DNS in Tun Mode, normal mod
<source>Timeout (s)</source> <source>Timeout (s)</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Automatic update</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Interval (minute, invalid if less than 30)</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>DialogEditGroup</name> <name>DialogEditGroup</name>
@@ -310,6 +318,18 @@ For NekoBox, this rewrites the underlying(localhost) DNS in Tun Mode, normal mod
<source>None</source> <source>None</source>
<translation>Нет</translation> <translation>Нет</translation>
</message> </message>
<message>
<source>Skip automatic update</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Common</source>
<translation type="unfinished">Общие</translation>
</message>
<message>
<source>Share</source>
<translation type="unfinished">Поделиться</translation>
</message>
</context> </context>
<context> <context>
<name>DialogEditProfile</name> <name>DialogEditProfile</name>

View File

@@ -243,6 +243,14 @@ For NekoBox, this rewrites the underlying(localhost) DNS in Tun Mode, normal mod
<source>Timeout (s)</source> <source>Timeout (s)</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>Automatic update</source>
<translation></translation>
</message>
<message>
<source>Interval (minute, invalid if less than 30)</source>
<translation> 30 </translation>
</message>
</context> </context>
<context> <context>
<name>DialogEditGroup</name> <name>DialogEditGroup</name>
@@ -310,6 +318,18 @@ For NekoBox, this rewrites the underlying(localhost) DNS in Tun Mode, normal mod
<source>Clear</source> <source>Clear</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>Skip automatic update</source>
<translation></translation>
</message>
<message>
<source>Common</source>
<translation></translation>
</message>
<message>
<source>Share</source>
<translation></translation>
</message>
</context> </context>
<context> <context>
<name>DialogEditProfile</name> <name>DialogEditProfile</name>

View File

@@ -12,6 +12,7 @@
#include <QFileDialog> #include <QFileDialog>
#include <QInputDialog> #include <QInputDialog>
#include <QMessageBox> #include <QMessageBox>
#include <QTimer>
class ExtraCoreWidget : public QWidget { class ExtraCoreWidget : public QWidget {
public: public:
@@ -158,6 +159,7 @@ DialogBasicSettings::DialogBasicSettings(QWidget *parent)
D_LOAD_BOOL(sub_use_proxy) D_LOAD_BOOL(sub_use_proxy)
D_LOAD_BOOL(sub_clear) D_LOAD_BOOL(sub_clear)
D_LOAD_BOOL(sub_insecure) D_LOAD_BOOL(sub_insecure)
D_LOAD_INT_ENABLE(sub_auto_update, sub_auto_update_enable)
// Core // Core
@@ -298,10 +300,17 @@ void DialogBasicSettings::accept() {
// Subscription // Subscription
if (ui->sub_auto_update_enable->isChecked()) {
TM_auto_update_subsctiption_Reset_Minute(ui->sub_auto_update->text().toInt());
} else {
TM_auto_update_subsctiption_Reset_Minute(0);
}
NekoGui::dataStore->user_agent = ui->user_agent->text(); NekoGui::dataStore->user_agent = ui->user_agent->text();
D_SAVE_BOOL(sub_use_proxy) D_SAVE_BOOL(sub_use_proxy)
D_SAVE_BOOL(sub_clear) D_SAVE_BOOL(sub_clear)
D_SAVE_BOOL(sub_insecure) D_SAVE_BOOL(sub_insecure)
D_SAVE_INT_ENABLE(sub_auto_update, sub_auto_update_enable)
// Core // Core

View File

@@ -486,37 +486,84 @@
<string>Subscription</string> <string>Subscription</string>
</attribute> </attribute>
<layout class="QGridLayout" name="gridLayout_3"> <layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0"> <item row="0" column="1">
<widget class="QLabel" name="label_4"> <layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QCheckBox" name="sub_auto_update_enable">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text"> <property name="text">
<string>User Agent</string> <string>Enable</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item>
<widget class="MyLineEdit" name="user_agent"/> <widget class="QLabel" name="label_21">
<property name="text">
<string>Interval (minute, invalid if less than 30)</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="sub_auto_update">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item> </item>
<item row="1" column="1"> <item row="1" column="1">
<widget class="MyLineEdit" name="user_agent"/>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="sub_use_proxy"> <widget class="QCheckBox" name="sub_use_proxy">
<property name="text"> <property name="text">
<string>Use proxy when updating subscription</string> <string>Use proxy when updating subscription</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="3" column="1">
<widget class="QCheckBox" name="sub_insecure"> <widget class="QCheckBox" name="sub_insecure">
<property name="text"> <property name="text">
<string>Ignore TLS errors when updating subscription</string> <string>Ignore TLS errors when updating subscription</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="4" column="1">
<widget class="QCheckBox" name="sub_clear"> <widget class="QCheckBox" name="sub_clear">
<property name="text"> <property name="text">
<string>Clear servers before updating subscription</string> <string>Clear servers before updating subscription</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="0">
<widget class="QLabel" name="label_20">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Automatic update</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>User Agent</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="tab_4"> <widget class="QWidget" name="tab_4">

View File

@@ -53,34 +53,6 @@ void DialogManageGroups::on_add_clicked() {
void DialogManageGroups::on_update_all_clicked() { void DialogManageGroups::on_update_all_clicked() {
if (QMessageBox::question(this, tr("Confirmation"), tr("Update all subscriptions?")) == QMessageBox::StandardButton::Yes) { if (QMessageBox::question(this, tr("Confirmation"), tr("Update all subscriptions?")) == QMessageBox::StandardButton::Yes) {
for (const auto &gid: NekoGui::profileManager->groupsTabOrder) { UI_update_all_groups();
auto group = NekoGui::profileManager->GetGroup(gid);
if (group == nullptr || group->url.isEmpty()) continue;
UI_update_one_group(NekoGui::profileManager->groupsTabOrder.indexOf(gid));
return;
} }
} }
}
void UI_update_one_group(int _order) {
// calculate next group
int nextOrder = _order;
std::shared_ptr<NekoGui::Group> nextGroup;
forever {
nextOrder += 1;
if (nextOrder >= NekoGui::profileManager->groupsTabOrder.size()) break;
auto nextGid = NekoGui::profileManager->groupsTabOrder[nextOrder];
nextGroup = NekoGui::profileManager->GetGroup(nextGid);
if (nextGroup == nullptr || nextGroup->url.isEmpty()) continue;
break;
}
// calculate this group
auto group = NekoGui::profileManager->GetGroup(NekoGui::profileManager->groupsTabOrder[_order]);
if (group == nullptr) return;
// v2.2: listener is moved to GroupItem, no refresh here.
NekoGui_sub::groupUpdater->AsyncUpdate(group->url, group->id, [=] {
if (nextGroup != nullptr) UI_update_one_group(nextOrder);
});
}

View File

@@ -30,7 +30,3 @@ private slots:
void on_update_all_clicked(); void on_update_all_clicked();
}; };
// 更新所有订阅 关闭分组窗口时 更新动作继续执行
void UI_update_one_group(int _order);

View File

@@ -6,28 +6,30 @@
#include <QClipboard> #include <QClipboard>
#define ADJUST_SIZE runOnUiThread([=] { adjustSize(); adjustPosition(mainwindow); }, this);
DialogEditGroup::DialogEditGroup(const std::shared_ptr<NekoGui::Group> &ent, QWidget *parent) : QDialog(parent), ui(new Ui::DialogEditGroup) { DialogEditGroup::DialogEditGroup(const std::shared_ptr<NekoGui::Group> &ent, QWidget *parent) : QDialog(parent), ui(new Ui::DialogEditGroup) {
ui->setupUi(this); ui->setupUi(this);
this->ent = ent; this->ent = ent;
connect(ui->type, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [=](int index) { connect(ui->type, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [=](int index) {
ui->cat_sub->setHidden(index == 0); ui->cat_sub->setHidden(index == 0);
ADJUST_SIZE
}); });
ui->name->setText(ent->name); ui->name->setText(ent->name);
ui->archive->setChecked(ent->archive); ui->archive->setChecked(ent->archive);
ui->skip_auto_update->setChecked(ent->skip_auto_update);
ui->url->setText(ent->url); ui->url->setText(ent->url);
ui->type->setCurrentIndex(ent->url.isEmpty() ? 0 : 1); ui->type->setCurrentIndex(ent->url.isEmpty() ? 0 : 1);
ui->type->currentIndexChanged(ui->type->currentIndex()); ui->type->currentIndexChanged(ui->type->currentIndex());
ui->manually_column_width->setChecked(ent->manually_column_width); ui->manually_column_width->setChecked(ent->manually_column_width);
ui->copy_links->setVisible(false); ui->cat_share->setVisible(false);
ui->copy_links_nkr->setVisible(false);
if (ent->id >= 0) { // already a group if (ent->id >= 0) { // already a group
ui->type->setDisabled(true); ui->type->setDisabled(true);
if (!ent->Profiles().isEmpty()) { if (!ent->Profiles().isEmpty()) {
ui->copy_links->setVisible(true); ui->cat_share->setVisible(true);
ui->copy_links_nkr->setVisible(true);
} }
} else { // new group } else { // new group
ui->front_proxy->hide(); ui->front_proxy->hide();
@@ -60,6 +62,8 @@ DialogEditGroup::DialogEditGroup(const std::shared_ptr<NekoGui::Group> &ent, QWi
QApplication::clipboard()->setText(links.join("\n")); QApplication::clipboard()->setText(links.join("\n"));
MessageBoxInfo(software_name, tr("Copied")); MessageBoxInfo(software_name, tr("Copied"));
}); });
ADJUST_SIZE
} }
DialogEditGroup::~DialogEditGroup() { DialogEditGroup::~DialogEditGroup() {
@@ -76,6 +80,7 @@ void DialogEditGroup::accept() {
ent->name = ui->name->text(); ent->name = ui->name->text();
ent->url = ui->url->text(); ent->url = ui->url->text();
ent->archive = ui->archive->isChecked(); ent->archive = ui->archive->isChecked();
ent->skip_auto_update = ui->skip_auto_update->isChecked();
ent->manually_column_width = ui->manually_column_width->isChecked(); ent->manually_column_width = ui->manually_column_width->isChecked();
ent->front_proxy_id = CACHE.front_proxy; ent->front_proxy_id = CACHE.front_proxy;
QDialog::accept(); QDialog::accept();

View File

@@ -7,62 +7,34 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>400</width> <width>400</width>
<height>300</height> <height>468</height>
</rect> </rect>
</property> </property>
<property name="minimumSize">
<size>
<width>400</width>
<height>300</height>
</size>
</property>
<property name="windowTitle"> <property name="windowTitle">
<string>Edit Group</string> <string>Edit Group</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="QWidget" name="cat_common" native="true"> <widget class="QGroupBox" name="cat_common">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="title">
<string>Common</string>
</property>
<layout class="QGridLayout" name="main"> <layout class="QGridLayout" name="main">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="1"> <item row="0" column="1">
<widget class="QLineEdit" name="name"/> <widget class="QLineEdit" name="name"/>
</item> </item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QCheckBox" name="manually_column_width">
<property name="text">
<string>Manually column width</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="archive">
<property name="text">
<string>Archive</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QLabel" name="label_2"> <widget class="QLabel" name="label_2">
<property name="text"> <property name="text">
@@ -91,6 +63,31 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QCheckBox" name="manually_column_width">
<property name="text">
<string>Manually column width</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="archive">
<property name="text">
<string>Archive</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="2" column="1"> <item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
@@ -119,28 +116,11 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QWidget" name="cat_sub" native="true"> <widget class="QGroupBox" name="cat_sub">
<property name="sizePolicy"> <property name="title">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <string>Subscription</string>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QGridLayout" name="_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="label_4"> <widget class="QLabel" name="label_4">
<property name="text"> <property name="text">
@@ -151,12 +131,21 @@
<item row="0" column="1"> <item row="0" column="1">
<widget class="MyLineEdit" name="url"/> <widget class="MyLineEdit" name="url"/>
</item> </item>
</layout> <item row="1" column="1">
<widget class="QCheckBox" name="skip_auto_update">
<property name="text">
<string>Skip automatic update</string>
</property>
</widget>
</item> </item>
</layout> </layout>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QGroupBox" name="cat_share">
<property name="title">
<string>Share</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout_2">
<item> <item>
<widget class="QPushButton" name="copy_links"> <widget class="QPushButton" name="copy_links">
@@ -173,6 +162,7 @@
</widget> </widget>
</item> </item>
</layout> </layout>
</widget>
</item> </item>
<item> <item>
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QDialogButtonBox" name="buttonBox">
@@ -198,6 +188,7 @@
<tabstop>manually_column_width</tabstop> <tabstop>manually_column_width</tabstop>
<tabstop>archive</tabstop> <tabstop>archive</tabstop>
<tabstop>url</tabstop> <tabstop>url</tabstop>
<tabstop>skip_auto_update</tabstop>
<tabstop>copy_links</tabstop> <tabstop>copy_links</tabstop>
<tabstop>copy_links_nkr</tabstop> <tabstop>copy_links_nkr</tabstop>
</tabstops> </tabstops>

View File

@@ -434,6 +434,14 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
connect(t, &QTimer::timeout, this, [&] { NekoGui_sys::logCounter.fetchAndStoreRelaxed(0); }); connect(t, &QTimer::timeout, this, [&] { NekoGui_sys::logCounter.fetchAndStoreRelaxed(0); });
t->start(1000); t->start(1000);
TM_auto_update_subsctiption = new QTimer;
TM_auto_update_subsctiption_Reset_Minute = [&](int m) {
TM_auto_update_subsctiption->stop();
if (m >= 30) TM_auto_update_subsctiption->start(m * 60 * 1000);
};
connect(TM_auto_update_subsctiption, &QTimer::timeout, this, [&] { UI_update_all_groups(true); });
TM_auto_update_subsctiption_Reset_Minute(NekoGui::dataStore->sub_auto_update);
if (!NekoGui::dataStore->flag_tray) show(); if (!NekoGui::dataStore->flag_tray) show();
} }