greader plugin now has ability to export/import feeds via API usage
This commit is contained in:
parent
95df21e385
commit
edc30fbe48
@ -241,6 +241,7 @@ void FeedsView::clearSelectedItems() {
|
||||
{},
|
||||
QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No,
|
||||
QMessageBox::StandardButton::No) != QMessageBox::StandardButton::Yes) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto* it : selectedItems()) {
|
||||
@ -257,6 +258,7 @@ void FeedsView::clearAllItems() {
|
||||
{},
|
||||
QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No,
|
||||
QMessageBox::StandardButton::No) != QMessageBox::StandardButton::Yes) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_sourceModel->markItemCleared(m_sourceModel->rootItem(), false);
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "gui/notifications/basetoastnotification.h"
|
||||
|
||||
#include "miscellaneous/iconfactory.h"
|
||||
#include "miscellaneous/settings.h"
|
||||
|
||||
#include <QCloseEvent>
|
||||
#include <QTimer>
|
||||
@ -14,7 +15,7 @@ using namespace std::chrono_literals;
|
||||
|
||||
BaseToastNotification::BaseToastNotification(QWidget* parent) : QDialog(parent), m_timerId(-1) {
|
||||
setAttribute(Qt::WidgetAttribute::WA_ShowWithoutActivating);
|
||||
setFixedWidth(NOTIFICATIONS_WIDTH);
|
||||
setFixedWidth(qApp->settings()->value(GROUP(GUI), SETTING(GUI::ToastNotificationsWidth)).toInt());
|
||||
setFocusPolicy(Qt::FocusPolicy::NoFocus);
|
||||
|
||||
setAttribute(Qt::WidgetAttribute::WA_DeleteOnClose, false);
|
||||
|
@ -56,6 +56,8 @@ void ToastNotificationsManager::setPosition(NotificationPosition position) {
|
||||
m_position = position;
|
||||
}
|
||||
|
||||
void ToastNotificationsManager::resetNotifications() {}
|
||||
|
||||
void ToastNotificationsManager::clear() {
|
||||
for (BaseToastNotification* notif : m_activeNotifications) {
|
||||
closeNotification(notif, true);
|
||||
|
@ -42,6 +42,8 @@ class ToastNotificationsManager : public QObject {
|
||||
NotificationPosition position() const;
|
||||
void setPosition(NotificationPosition position);
|
||||
|
||||
void resetNotifications();
|
||||
|
||||
public slots:
|
||||
void clear();
|
||||
void showNotification(Notification::Event event, const GuiMessage& msg, const GuiAction& action);
|
||||
|
@ -17,11 +17,12 @@
|
||||
SettingsLocalization::SettingsLocalization(Settings* settings, QWidget* parent)
|
||||
: SettingsPanel(settings, parent), m_ui(new Ui::SettingsLocalization) {
|
||||
m_ui->setupUi(this);
|
||||
m_ui->m_treeLanguages->setColumnCount(4);
|
||||
m_ui->m_lblAuthors->label()->setWordWrap(true);
|
||||
m_ui->m_treeLanguages->setColumnCount(3);
|
||||
m_ui->m_treeLanguages->setHeaderHidden(false);
|
||||
m_ui->m_treeLanguages->setHeaderLabels(QStringList() << /*: Language column of language list. */ tr("Language")
|
||||
<< /*: Lang. code column of language list. */ tr("Code")
|
||||
<< tr("Translation progress") << tr("Author"));
|
||||
<< tr("Translation progress"));
|
||||
|
||||
m_ui->m_lblHelp->setText(tr(R"(Help us to improve %1 <a href="%2">translations</a>.)")
|
||||
.arg(QSL(APP_NAME), QSL("https://crowdin.com/project/rssguard")));
|
||||
@ -32,7 +33,6 @@ SettingsLocalization::SettingsLocalization(Settings* settings, QWidget* parent)
|
||||
m_ui->m_treeLanguages->header()->setSectionResizeMode(0, QHeaderView::ResizeMode::ResizeToContents);
|
||||
m_ui->m_treeLanguages->header()->setSectionResizeMode(1, QHeaderView::ResizeMode::ResizeToContents);
|
||||
m_ui->m_treeLanguages->header()->setSectionResizeMode(2, QHeaderView::ResizeMode::ResizeToContents);
|
||||
m_ui->m_treeLanguages->header()->setSectionResizeMode(3, QHeaderView::ResizeMode::ResizeToContents);
|
||||
|
||||
connect(m_ui->m_treeLanguages, &QTreeWidget::currentItemChanged, this, &SettingsLocalization::requireRestart);
|
||||
connect(m_ui->m_treeLanguages, &QTreeWidget::currentItemChanged, this, &SettingsLocalization::dirtifySettings);
|
||||
@ -48,20 +48,46 @@ void SettingsLocalization::loadSettings() {
|
||||
auto langs = qApp->localization()->installedLanguages();
|
||||
|
||||
// Also, load statistics with restricted access token.
|
||||
QByteArray stats_out;
|
||||
QList<QPair<QByteArray, QByteArray>> hdrs = {
|
||||
{"Authorization",
|
||||
"Bearer "
|
||||
"0fbcad4c39d21a55f63f8a1b6d07cc56bb1e2eb2047bfaf1ee22425e3edf1c2b217f4d13b3cebba9"}};
|
||||
QByteArray stats_out, people_out;
|
||||
QMap<QString, int> percentages_langs;
|
||||
QString all_translators;
|
||||
|
||||
NetworkResult stats_res = NetworkFactory::
|
||||
performNetworkOperation(QSL("https://api.crowdin.com/api/v2/projects/608575/languages/progress"),
|
||||
performNetworkOperation(QSL("https://api.crowdin.com/api/v2/projects/608575/languages/progress?limit=100"),
|
||||
qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(),
|
||||
{},
|
||||
stats_out,
|
||||
QNetworkAccessManager::Operation::GetOperation,
|
||||
{{"Authorization",
|
||||
"Bearer "
|
||||
"0fbcad4c39d21a55f63f8a1b6d07cc56bb1e2eb2047bfaf1ee22425e3edf1c2b217f4d13b3cebba9"}});
|
||||
hdrs);
|
||||
|
||||
NetworkResult people_res =
|
||||
NetworkFactory::performNetworkOperation(QSL("https://api.crowdin.com/api/v2/projects/608575/members?limit=500"),
|
||||
qApp->settings()
|
||||
->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout))
|
||||
.toInt(),
|
||||
{},
|
||||
people_out,
|
||||
QNetworkAccessManager::Operation::GetOperation,
|
||||
hdrs);
|
||||
|
||||
if (stats_res.m_networkError == QNetworkReply::NetworkError::NoError) {
|
||||
QJsonDocument stats_doc = QJsonDocument::fromJson(stats_out);
|
||||
QJsonDocument people_doc = QJsonDocument::fromJson(people_out);
|
||||
QJsonArray people_arr = people_doc.object()["data"].toArray();
|
||||
std::vector<QString> people_desc;
|
||||
|
||||
std::transform(people_arr.begin(), people_arr.end(), std::back_inserter(people_desc), [](const QJsonValue& b) {
|
||||
return b.toObject()["data"].toObject()["username"].toString();
|
||||
});
|
||||
|
||||
all_translators =
|
||||
std::accumulate(std::next(people_desc.begin()), people_desc.end(), people_desc.at(0), [](auto lhs, auto rhs) {
|
||||
return std::move(lhs) + ", " + rhs;
|
||||
});
|
||||
|
||||
for (const QJsonValue& val_lang : stats_doc.object()["data"].toArray()) {
|
||||
QString lang_id = val_lang.toObject()["data"].toObject()["languageId"].toString().replace(QSL("-"), QSL("_"));
|
||||
@ -75,6 +101,17 @@ void SettingsLocalization::loadSettings() {
|
||||
}
|
||||
}
|
||||
|
||||
if (all_translators.isEmpty()) {
|
||||
m_ui->m_lblAuthors->setStatus(WidgetWithStatus::StatusType::Information,
|
||||
tr("Big thanks to all translators!"),
|
||||
tr("Big thanks to all translators!"));
|
||||
}
|
||||
else {
|
||||
m_ui->m_lblAuthors->setStatus(WidgetWithStatus::StatusType::Information,
|
||||
tr("Translations provided by: %1").arg(all_translators),
|
||||
tr("Big thanks to all translators!"));
|
||||
}
|
||||
|
||||
for (const Language& language : qAsConst(langs)) {
|
||||
auto* item = new QTreeWidgetItem(m_ui->m_treeLanguages);
|
||||
int perc_translated = percentages_langs.value(language.m_code);
|
||||
@ -87,7 +124,6 @@ void SettingsLocalization::loadSettings() {
|
||||
}
|
||||
|
||||
item->setText(2, QSL("%1 %").arg(perc_translated >= 0 ? QString::number(perc_translated) : QSL("-")));
|
||||
item->setText(3, language.m_author);
|
||||
item->setIcon(0, qApp->icons()->miscIcon(QSL(FLAG_ICON_SUBFOLDER) + QDir::separator() + language.m_code));
|
||||
|
||||
QColor col_translated = QColor::fromHsv(perc_translated, 200, 230);
|
||||
|
@ -36,11 +36,22 @@
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="LabelWithStatus" name="m_lblAuthors" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="m_lblHelp"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>LabelWithStatus</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>labelwithstatus.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
@ -30,6 +30,9 @@ SettingsNotifications::SettingsNotifications(Settings* settings, QWidget* parent
|
||||
connect(m_ui.m_sbScreen, QOverload<int>::of(&QSpinBox::valueChanged), this, &SettingsNotifications::dirtifySettings);
|
||||
connect(m_ui.m_sbScreen, QOverload<int>::of(&QSpinBox::valueChanged), this, &SettingsNotifications::requireRestart);
|
||||
|
||||
connect(m_ui.m_sbMargin, QOverload<int>::of(&QSpinBox::valueChanged), this, &SettingsNotifications::dirtifySettings);
|
||||
connect(m_ui.m_sbWidth, QOverload<int>::of(&QSpinBox::valueChanged), this, &SettingsNotifications::dirtifySettings);
|
||||
|
||||
connect(m_ui.m_sbScreen, QOverload<int>::of(&QSpinBox::valueChanged), this, &SettingsNotifications::showScreenInfo);
|
||||
|
||||
connect(m_ui.m_cbCustomNotificationsPosition,
|
||||
@ -65,6 +68,8 @@ void SettingsNotifications::loadSettings() {
|
||||
m_ui.m_rbNativeNotifications
|
||||
->setChecked(!settings()->value(GROUP(GUI), SETTING(GUI::UseToastNotifications)).toBool());
|
||||
m_ui.m_sbScreen->setValue(settings()->value(GROUP(GUI), SETTING(GUI::ToastNotificationsScreen)).toInt());
|
||||
m_ui.m_sbWidth->setValue(settings()->value(GROUP(GUI), SETTING(GUI::ToastNotificationsWidth)).toInt());
|
||||
m_ui.m_sbMargin->setValue(settings()->value(GROUP(GUI), SETTING(GUI::ToastNotificationsMargin)).toInt());
|
||||
|
||||
m_ui.m_cbCustomNotificationsPosition
|
||||
->setCurrentIndex(m_ui.m_cbCustomNotificationsPosition
|
||||
@ -84,12 +89,15 @@ void SettingsNotifications::saveSettings() {
|
||||
|
||||
settings()->setValue(GROUP(GUI), GUI::UseToastNotifications, m_ui.m_rbCustomNotifications->isChecked());
|
||||
settings()->setValue(GROUP(GUI), GUI::ToastNotificationsScreen, m_ui.m_sbScreen->value());
|
||||
settings()->setValue(GROUP(GUI), GUI::ToastNotificationsWidth, m_ui.m_sbWidth->value());
|
||||
settings()->setValue(GROUP(GUI), GUI::ToastNotificationsMargin, m_ui.m_sbMargin->value());
|
||||
|
||||
settings()->setValue(GROUP(GUI),
|
||||
GUI::ToastNotificationsPosition,
|
||||
m_ui.m_cbCustomNotificationsPosition->currentData()
|
||||
.value<ToastNotificationsManager::NotificationPosition>());
|
||||
|
||||
// qApp->m_toastNotifications
|
||||
onEndSaveSettings();
|
||||
}
|
||||
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>367</width>
|
||||
<height>300</height>
|
||||
<width>407</width>
|
||||
<height>357</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
@ -76,27 +76,64 @@
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="m_cbCustomNotificationsPosition"/>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<item row="3" column="1">
|
||||
<widget class="QSpinBox" name="m_sbScreen">
|
||||
<property name="value">
|
||||
<number>99</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Screen</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<item row="4" column="1">
|
||||
<widget class="QLabel" name="m_lblScreenInfo">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="m_sbWidth">
|
||||
<property name="suffix">
|
||||
<string> px</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>50</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>1000</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Width</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QSpinBox" name="m_sbMargin">
|
||||
<property name="suffix">
|
||||
<string> px</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Margins</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -149,9 +149,6 @@ Application::Application(const QString& id, int& argc, char** argv, const QStrin
|
||||
|
||||
determineFirstRuns();
|
||||
|
||||
//: Name of translator - optional.
|
||||
QObject::tr("LANG_AUTHOR");
|
||||
|
||||
// Add an extra path for non-system icon themes and set current icon theme
|
||||
// and skin.
|
||||
m_icons->setupSearchPaths();
|
||||
|
@ -27,7 +27,7 @@ void Localization::loadActiveLanguage() {
|
||||
<< QUOTE_W_SPACE_DOT(desired_localization);
|
||||
|
||||
if (app_translator->load(QLocale(desired_localization), QSL("rssguard"), QSL("_"), APP_LANG_PATH)) {
|
||||
const QString real_loaded_locale = app_translator->translate("QObject", "LANG_ABBREV");
|
||||
const QString real_loaded_locale = app_translator->language();
|
||||
|
||||
QCoreApplication::installTranslator(app_translator);
|
||||
|
||||
@ -76,8 +76,7 @@ QList<Language> Localization::installedLanguages() const {
|
||||
if (translator.load(file.absoluteFilePath())) {
|
||||
Language new_language;
|
||||
|
||||
new_language.m_code = translator.language().replace(QSL("-"), QSL("_"));
|
||||
new_language.m_author = translator.translate("QObject", "LANG_AUTHOR");
|
||||
new_language.m_code = translator.language();
|
||||
new_language.m_name = QLocale(new_language.m_code).nativeLanguageName();
|
||||
languages << new_language;
|
||||
}
|
||||
|
@ -11,7 +11,6 @@
|
||||
struct Language {
|
||||
QString m_name;
|
||||
QString m_code;
|
||||
QString m_author;
|
||||
};
|
||||
|
||||
class RSSGUARD_DLLSPEC Localization : public QObject {
|
||||
|
@ -295,6 +295,12 @@ GUI::ToastNotificationsPositionDef = ToastNotificationsManager::NotificationPosi
|
||||
DKEY GUI::ToastNotificationsScreen = "toast_notifications_screen";
|
||||
DVALUE(int) GUI::ToastNotificationsScreenDef = -1;
|
||||
|
||||
DKEY GUI::ToastNotificationsMargin = "toast_notifications_margin";
|
||||
DVALUE(int) GUI::ToastNotificationsMarginDef = NOTIFICATIONS_MARGIN;
|
||||
|
||||
DKEY GUI::ToastNotificationsWidth = "toast_notifications_width";
|
||||
DVALUE(int) GUI::ToastNotificationsWidthDef = NOTIFICATIONS_WIDTH;
|
||||
|
||||
DKEY GUI::HideMainWindowWhenMinimized = "hide_when_minimized";
|
||||
DVALUE(bool) GUI::HideMainWindowWhenMinimizedDef = false;
|
||||
|
||||
|
@ -228,6 +228,12 @@ namespace GUI {
|
||||
KEY ToastNotificationsScreen;
|
||||
VALUE(int) ToastNotificationsScreenDef;
|
||||
|
||||
KEY ToastNotificationsMargin;
|
||||
VALUE(int) ToastNotificationsMarginDef;
|
||||
|
||||
KEY ToastNotificationsWidth;
|
||||
VALUE(int) ToastNotificationsWidthDef;
|
||||
|
||||
KEY MessageViewState;
|
||||
VALUE(QString) MessageViewStateDef;
|
||||
|
||||
|
@ -4,9 +4,9 @@
|
||||
#define GREADER_DEFAULT_BATCH_SIZE 100
|
||||
|
||||
// URLs.
|
||||
#define GREADER_URL_REEDAH "https://www.reedah.com"
|
||||
#define GREADER_URL_TOR "https://theoldreader.com"
|
||||
#define GREADER_URL_BAZQUX "https://bazqux.com"
|
||||
#define GREADER_URL_REEDAH "https://www.reedah.com"
|
||||
#define GREADER_URL_TOR "https://theoldreader.com"
|
||||
#define GREADER_URL_BAZQUX "https://bazqux.com"
|
||||
#define GREADER_URL_INOREADER "https://www.inoreader.com"
|
||||
|
||||
// States.
|
||||
@ -14,29 +14,31 @@
|
||||
|
||||
// Means "read" message. If both "reading-list" and "read" are specified, message is READ. If this state
|
||||
// is not present, message is UNREAD.
|
||||
#define GREADER_API_STATE_READ "state/com.google/read"
|
||||
#define GREADER_API_STATE_READ "state/com.google/read"
|
||||
#define GREADER_API_STATE_IMPORTANT "state/com.google/starred"
|
||||
|
||||
#define GREADER_API_FULL_STATE_READING_LIST "user/-/state/com.google/reading-list"
|
||||
#define GREADER_API_FULL_STATE_READ "user/-/state/com.google/read"
|
||||
#define GREADER_API_FULL_STATE_IMPORTANT "user/-/state/com.google/starred"
|
||||
#define GREADER_API_FULL_STATE_READ "user/-/state/com.google/read"
|
||||
#define GREADER_API_FULL_STATE_IMPORTANT "user/-/state/com.google/starred"
|
||||
|
||||
// API.
|
||||
#define GREADER_API_CLIENT_LOGIN "accounts/ClientLogin"
|
||||
#define GREADER_API_TAG_LIST "reader/api/0/tag/list?output=json"
|
||||
#define GREADER_API_SUBSCRIPTION_LIST "reader/api/0/subscription/list?output=json"
|
||||
#define GREADER_API_STREAM_CONTENTS "reader/api/0/stream/contents/%1?output=json&n=%2"
|
||||
#define GREADER_API_EDIT_TAG "reader/api/0/edit-tag"
|
||||
#define GREADER_API_ITEM_IDS "reader/api/0/stream/items/ids?output=json&n=%2&s=%1"
|
||||
#define GREADER_API_ITEM_CONTENTS "reader/api/0/stream/items/contents?output=json&n=200000"
|
||||
#define GREADER_API_TOKEN "reader/api/0/token"
|
||||
#define GREADER_API_USER_INFO "reader/api/0/user-info?output=json"
|
||||
#define GREADER_API_CLIENT_LOGIN "accounts/ClientLogin"
|
||||
#define GREADER_API_TAG_LIST "reader/api/0/tag/list?output=json"
|
||||
#define GREADER_API_SUBSCRIPTION_LIST "reader/api/0/subscription/list?output=json"
|
||||
#define GREADER_API_STREAM_CONTENTS "reader/api/0/stream/contents/%1?output=json&n=%2"
|
||||
#define GREADER_API_EDIT_TAG "reader/api/0/edit-tag"
|
||||
#define GREADER_API_SUBSCRIPTION_EXPORT "reader/api/0/subscription/export"
|
||||
#define GREADER_API_SUBSCRIPTION_IMPORT "reader/api/0/subscription/import"
|
||||
#define GREADER_API_ITEM_IDS "reader/api/0/stream/items/ids?output=json&n=%2&s=%1"
|
||||
#define GREADER_API_ITEM_CONTENTS "reader/api/0/stream/items/contents?output=json&n=200000"
|
||||
#define GREADER_API_TOKEN "reader/api/0/token"
|
||||
#define GREADER_API_USER_INFO "reader/api/0/user-info?output=json"
|
||||
|
||||
// Misc.
|
||||
#define GREADET_API_ITEM_IDS_MAX 200000
|
||||
#define GREADER_API_EDIT_TAG_BATCH 200
|
||||
#define GREADER_API_ITEM_IDS_MAX 200000
|
||||
#define GREADER_API_EDIT_TAG_BATCH 200
|
||||
#define GREADER_API_ITEM_CONTENTS_BATCH 999
|
||||
#define GREADER_GLOBAL_UPDATE_THRES 0.3
|
||||
#define GREADER_GLOBAL_UPDATE_THRES 0.3
|
||||
|
||||
// The Old Reader.
|
||||
#define TOR_SPONSORED_STREAM_ID "tor/sponsored"
|
||||
@ -45,14 +47,14 @@
|
||||
// Inoreader.
|
||||
#define INO_ITEM_CONTENTS_BATCH 250
|
||||
|
||||
#define INO_HEADER_APPID "AppId"
|
||||
#define INO_HEADER_APPID "AppId"
|
||||
#define INO_HEADER_APPKEY "AppKey"
|
||||
|
||||
#define INO_OAUTH_REDIRECT_URI_PORT 14488
|
||||
#define INO_OAUTH_SCOPE "read write"
|
||||
#define INO_OAUTH_TOKEN_URL "https://www.inoreader.com/oauth2/token"
|
||||
#define INO_OAUTH_AUTH_URL "https://www.inoreader.com/oauth2/auth"
|
||||
#define INO_REG_API_URL "https://www.inoreader.com/developers/register-app"
|
||||
#define INO_OAUTH_SCOPE "read write"
|
||||
#define INO_OAUTH_TOKEN_URL "https://www.inoreader.com/oauth2/token"
|
||||
#define INO_OAUTH_AUTH_URL "https://www.inoreader.com/oauth2/auth"
|
||||
#define INO_REG_API_URL "https://www.inoreader.com/developers/register-app"
|
||||
|
||||
// FreshRSS.
|
||||
#define FRESHRSS_BASE_URL_PATH "api/greader.php/"
|
||||
|
@ -341,6 +341,61 @@ QNetworkReply::NetworkError GreaderNetwork::markMessagesStarred(RootItem::Import
|
||||
proxy);
|
||||
}
|
||||
|
||||
void GreaderNetwork::subscriptionImport(const QByteArray& opml_data, const QNetworkProxy& proxy) {
|
||||
if (!ensureLogin(proxy)) {
|
||||
throw ApplicationException(tr("login failed"));
|
||||
}
|
||||
|
||||
QString full_url = generateFullUrl(Operations::SubscriptionImport);
|
||||
auto timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt();
|
||||
|
||||
QByteArray output;
|
||||
auto result = NetworkFactory::performNetworkOperation(full_url,
|
||||
timeout,
|
||||
opml_data,
|
||||
output,
|
||||
QNetworkAccessManager::Operation::PostOperation,
|
||||
{authHeader()},
|
||||
false,
|
||||
{},
|
||||
{},
|
||||
proxy);
|
||||
|
||||
if (result.m_networkError != QNetworkReply::NetworkError::NoError) {
|
||||
qCriticalNN << LOGSEC_GREADER << "Cannot get OPML data, network error:" << QUOTE_W_SPACE_DOT(result.m_networkError);
|
||||
throw NetworkException(result.m_networkError, output);
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray GreaderNetwork::subscriptionExport(const QNetworkProxy& proxy) {
|
||||
if (!ensureLogin(proxy)) {
|
||||
throw ApplicationException(tr("login failed"));
|
||||
}
|
||||
|
||||
QString full_url = generateFullUrl(Operations::SubscriptionExport);
|
||||
auto timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt();
|
||||
|
||||
QByteArray output;
|
||||
auto result = NetworkFactory::performNetworkOperation(full_url,
|
||||
timeout,
|
||||
{},
|
||||
output,
|
||||
QNetworkAccessManager::Operation::GetOperation,
|
||||
{authHeader()},
|
||||
false,
|
||||
{},
|
||||
{},
|
||||
proxy);
|
||||
|
||||
if (result.m_networkError != QNetworkReply::NetworkError::NoError) {
|
||||
qCriticalNN << LOGSEC_GREADER << "Cannot get OPML data, network error:" << QUOTE_W_SPACE_DOT(result.m_networkError);
|
||||
throw NetworkException(result.m_networkError, output);
|
||||
}
|
||||
else {
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
QStringList GreaderNetwork::itemIds(const QString& stream_id,
|
||||
bool unread_only,
|
||||
const QNetworkProxy& proxy,
|
||||
@ -357,7 +412,7 @@ QStringList GreaderNetwork::itemIds(const QString& stream_id,
|
||||
QString full_url =
|
||||
generateFullUrl(Operations::ItemIds)
|
||||
.arg(m_service == GreaderServiceRoot::Service::TheOldReader ? stream_id : QUrl::toPercentEncoding(stream_id),
|
||||
QString::number(max_count <= 0 ? GREADET_API_ITEM_IDS_MAX : max_count));
|
||||
QString::number(max_count <= 0 ? GREADER_API_ITEM_IDS_MAX : max_count));
|
||||
auto timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt();
|
||||
|
||||
if (unread_only) {
|
||||
@ -1066,6 +1121,12 @@ QString GreaderNetwork::generateFullUrl(GreaderNetwork::Operations operation) co
|
||||
case Operations::ClientLogin:
|
||||
return sanitizedBaseUrl() + QSL(GREADER_API_CLIENT_LOGIN);
|
||||
|
||||
case Operations::SubscriptionExport:
|
||||
return sanitizedBaseUrl() + QSL(GREADER_API_SUBSCRIPTION_EXPORT);
|
||||
|
||||
case Operations::SubscriptionImport:
|
||||
return sanitizedBaseUrl() + QSL(GREADER_API_SUBSCRIPTION_IMPORT);
|
||||
|
||||
case Operations::Token:
|
||||
return sanitizedBaseUrl() + QSL(GREADER_API_TOKEN);
|
||||
|
||||
|
@ -24,7 +24,9 @@ class GreaderNetwork : public QObject {
|
||||
Token,
|
||||
UserInfo,
|
||||
ItemIds,
|
||||
ItemContents
|
||||
ItemContents,
|
||||
SubscriptionExport,
|
||||
SubscriptionImport
|
||||
};
|
||||
|
||||
explicit GreaderNetwork(QObject* parent = nullptr);
|
||||
@ -82,6 +84,8 @@ class GreaderNetwork : public QObject {
|
||||
void setOauth(OAuth2Service* oauth);
|
||||
|
||||
// API methods.
|
||||
void subscriptionImport(const QByteArray& opml_data, const QNetworkProxy& proxy);
|
||||
QByteArray subscriptionExport(const QNetworkProxy& proxy);
|
||||
QNetworkReply::NetworkError editLabels(const QString& state,
|
||||
bool assign,
|
||||
const QStringList& msg_custom_ids,
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "database/databasequeries.h"
|
||||
#include "definitions/definitions.h"
|
||||
#include "gui/messagebox.h"
|
||||
#include "miscellaneous/application.h"
|
||||
#include "miscellaneous/iconfactory.h"
|
||||
#include "miscellaneous/textfactory.h"
|
||||
@ -13,6 +14,8 @@
|
||||
#include "services/greader/greadernetwork.h"
|
||||
#include "services/greader/gui/formeditgreaderaccount.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
|
||||
GreaderServiceRoot::GreaderServiceRoot(RootItem* parent) : ServiceRoot(parent), m_network(new GreaderNetwork(this)) {
|
||||
setIcon(GreaderEntryPoint().icon());
|
||||
m_network->setRoot(this);
|
||||
@ -130,6 +133,59 @@ QString GreaderServiceRoot::serviceToString(Service service) {
|
||||
}
|
||||
}
|
||||
|
||||
void GreaderServiceRoot::importFeeds() {
|
||||
const QString filter_opml20 = tr("OPML 2.0 files (*.opml *.xml)");
|
||||
const QString selected_file = QFileDialog::getOpenFileName(qApp->mainFormWidget(),
|
||||
tr("Select file for feeds import"),
|
||||
qApp->homeFolder(),
|
||||
filter_opml20);
|
||||
|
||||
if (!QFile::exists(selected_file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
m_network->subscriptionImport(IOFactory::readFile(selected_file), networkProxy());
|
||||
MsgBox::show(qApp->mainFormWidget(),
|
||||
QMessageBox::Icon::Information,
|
||||
tr("Done"),
|
||||
tr("Data imported successfully. Reloading feed tree."));
|
||||
|
||||
syncIn();
|
||||
}
|
||||
catch (const ApplicationException& ex) {
|
||||
MsgBox::show(qApp->mainFormWidget(),
|
||||
QMessageBox::Icon::Critical,
|
||||
tr("Cannot import feeds"),
|
||||
tr("Error: %1").arg(ex.message()));
|
||||
}
|
||||
}
|
||||
|
||||
void GreaderServiceRoot::exportFeeds() {
|
||||
const QString the_file = qApp->homeFolder() + QDir::separator() +
|
||||
QSL("rssguard_feeds_%1.opml").arg(QDate::currentDate().toString(Qt::DateFormat::ISODate));
|
||||
const QString filter_opml20 = tr("OPML 2.0 files (*.opml *.xml)");
|
||||
const QString selected_file =
|
||||
QFileDialog::getSaveFileName(qApp->mainFormWidget(), tr("Select file for feeds export"), the_file, filter_opml20);
|
||||
|
||||
if (selected_file.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
QByteArray data = m_network->subscriptionExport(networkProxy());
|
||||
IOFactory::writeFile(selected_file, data);
|
||||
|
||||
MsgBox::show(qApp->mainFormWidget(), QMessageBox::Icon::Information, tr("Done"), tr("Data exported successfully."));
|
||||
}
|
||||
catch (const ApplicationException& ex) {
|
||||
MsgBox::show(qApp->mainFormWidget(),
|
||||
QMessageBox::Icon::Critical,
|
||||
tr("Cannot export feeds"),
|
||||
tr("Error: %1").arg(ex.message()));
|
||||
}
|
||||
}
|
||||
|
||||
QList<Message> GreaderServiceRoot::obtainNewMessages(Feed* feed,
|
||||
const QHash<ServiceRoot::BagOfMessages, QStringList>&
|
||||
stated_messages,
|
||||
@ -178,6 +234,23 @@ QString GreaderServiceRoot::code() const {
|
||||
return GreaderEntryPoint().code();
|
||||
}
|
||||
|
||||
QList<QAction*> GreaderServiceRoot::serviceMenu() {
|
||||
if (m_serviceMenu.isEmpty()) {
|
||||
ServiceRoot::serviceMenu();
|
||||
|
||||
auto* action_export_feeds = new QAction(qApp->icons()->fromTheme(QSL("document-export")), tr("Export feeds"), this);
|
||||
auto* action_import_feeds = new QAction(qApp->icons()->fromTheme(QSL("document-import")), tr("Import feeds"), this);
|
||||
|
||||
connect(action_export_feeds, &QAction::triggered, this, &GreaderServiceRoot::exportFeeds);
|
||||
connect(action_import_feeds, &QAction::triggered, this, &GreaderServiceRoot::importFeeds);
|
||||
|
||||
m_serviceMenu.append(action_export_feeds);
|
||||
m_serviceMenu.append(action_import_feeds);
|
||||
}
|
||||
|
||||
return m_serviceMenu;
|
||||
}
|
||||
|
||||
void GreaderServiceRoot::saveAllCachedData(bool ignore_errors) {
|
||||
auto msg_cache = takeMessageCache();
|
||||
QMapIterator<RootItem::ReadStatus, QStringList> i(msg_cache.m_cachedStatesRead);
|
||||
|
@ -32,6 +32,7 @@ class GreaderServiceRoot : public ServiceRoot, public CacheForServiceRoot {
|
||||
virtual FormAccountDetails* accountSetupDialog() const;
|
||||
virtual void start(bool freshly_activated);
|
||||
virtual QString code() const;
|
||||
virtual QList<QAction*> serviceMenu();
|
||||
virtual void saveAllCachedData(bool ignore_errors);
|
||||
virtual LabelOperation supportedLabelOperations() const;
|
||||
virtual QVariantHash customDatabaseData() const;
|
||||
@ -49,6 +50,10 @@ class GreaderServiceRoot : public ServiceRoot, public CacheForServiceRoot {
|
||||
|
||||
static QString serviceToString(Service service);
|
||||
|
||||
private slots:
|
||||
void importFeeds();
|
||||
void exportFeeds();
|
||||
|
||||
protected:
|
||||
virtual RootItem* obtainNewTreeForSyncIn() const;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user