From 1b2d155ecad4eff596807475ddefd934e87c3b2b Mon Sep 17 00:00:00 2001 From: Martin Rotter Date: Tue, 23 Apr 2024 10:59:28 +0200 Subject: [PATCH] bit more polishment --- resources/text/CHANGELOG | 3 +- src/librssguard/gui/webbrowser.cpp | 64 ++++++++++++++++++- src/librssguard/gui/webbrowser.h | 4 +- .../qtextbrowser/textbrowserviewer.cpp | 24 ++++--- .../qtextbrowser/textbrowserviewer.h | 1 + .../webviewers/webengine/webengineviewer.cpp | 7 +- .../webviewers/webengine/webengineviewer.h | 1 + src/librssguard/gui/webviewers/webviewer.h | 3 + src/librssguard/network-web/articleparse.cpp | 17 +++-- src/librssguard/network-web/articleparse.h | 4 +- 10 files changed, 105 insertions(+), 23 deletions(-) diff --git a/resources/text/CHANGELOG b/resources/text/CHANGELOG index 59b58fd13..b8a6bd1e9 100644 --- a/resources/text/CHANGELOG +++ b/resources/text/CHANGELOG @@ -6,8 +6,9 @@ OK, dear users. Over recent releases, many features were added and as you can se Sole focus will be on fixing bugs and polish existing features and clean codebase. Added: +* Full article content can now be fetched with a single button. Button is placed in article viewer toolbar right next to "Reader mode" button. This feature (just as "reader mode" feature) requires Node.js installation. The feature works both for articles and for regular websites opened in embedded web browser. * Article now can be marked (upon selection) as read with delay or only manually. (#1017) -* All RSS Guard plugins/services are now placed in their own library (DLL/SO/DYLIB) files and are loaded by main RSS Guard library dynamically. This means that unused services can now be removed from RSS Guard installation if not used by the user. Also it allows for a cleaner and slimmer common codebase. Refactoring of main RSS Guard library was also done and it is now more usable as regular dynamic-link library. I expect some regressions as this was HUGE change. Also, this change allows new potential interested people in writing new plugins easier as they now can just copy one of existing plugins and tweak for new service. +* All RSS Guard plugins/services are now placed in their own library (DLL/SO/DYLIB) files and are loaded by main RSS Guard library dynamically. This means that unused services can now be removed from RSS Guard installation if not used by the user. Also it allows for a cleaner and slimmer common codebase. Refactoring of main RSS Guard library was also done and it is now more usable as regular dynamic-link library. I expect some regressions as this was HUGE change. Also, this change allows new potential interested people in writing new plugins easier as they now can just copy one of existing plugins and tweak for new service. Also this change now propagates to RSS Guard installer which allows you to only install plugins/services you want. * Application (Qt) style and icon theme now can be properly set to respect system style/icons and this setting is dynamic, meaning if you change system theme and restart RSS Guard, new theme is honored. (#1352) * Button to copy system/app information to "About..." dialog. (#1318) * All modal dialogs now remember their sizes forever. (#1336) diff --git a/src/librssguard/gui/webbrowser.cpp b/src/librssguard/gui/webbrowser.cpp index 4b661f0f0..78d9a1496 100644 --- a/src/librssguard/gui/webbrowser.cpp +++ b/src/librssguard/gui/webbrowser.cpp @@ -318,12 +318,70 @@ void WebBrowser::readabilityFailed(QObject* sndr, const QString& error) { } } -void WebBrowser::setFullArticleHtml(QObject* sndr, const QString& json_answer) { +Message WebBrowser::messageFromExtractor(const QJsonDocument& extracted_data) const { + QJsonObject extracted_obj = extracted_data.object(); + Message msg; + + msg.m_title = extracted_obj["title"].toString(); + msg.m_author = extracted_obj["author"].toString(); + msg.m_created = TextFactory::parseDateTime(extracted_obj["published"].toString()); + msg.m_createdFromFeed = true; + msg.m_url = extracted_obj["url"].toString(); + msg.m_contents = extracted_obj["content"].toString(); + + QString image = extracted_obj["image"].toString(); + + if (!image.isEmpty()) { + // NOTE: Prepend image to content. + msg.m_contents = msg.m_contents.prepend(QSL("
" + "" + "" + "" + "
") + .arg(image)); + } + + return msg; +} + +void WebBrowser::setFullArticleHtml(QObject* sndr, const QString& url, const QString& json_answer) { if (sndr == this && !json_answer.isEmpty()) { QJsonDocument json_doc = QJsonDocument::fromJson(json_answer.toUtf8()); - QString better_html = json_doc["content"].toString(); - m_webView->setReadabledHtml(better_html, m_webView->url()); + Message full_article = messageFromExtractor(json_doc); + + if (!m_messages.isEmpty() && m_messages.first().m_url == url) { + const Message displayed_article = m_messages.first(); + + // Copy rest of original attributes which might influence the "full" article. + full_article.m_feedId = displayed_article.m_feedId; + full_article.m_feedTitle = displayed_article.m_feedTitle; + full_article.m_customId = displayed_article.m_customId; + full_article.m_customHash = displayed_article.m_customHash; + full_article.m_id = displayed_article.m_id; + full_article.m_accountId = displayed_article.m_accountId; + full_article.m_assignedLabels = displayed_article.m_assignedLabels; + full_article.m_assignedLabelsIds = displayed_article.m_assignedLabelsIds; + full_article.m_categories = displayed_article.m_categories; + full_article.m_rawContents = displayed_article.m_rawContents; + + full_article.m_isRead = displayed_article.m_isRead; + full_article.m_isImportant = displayed_article.m_isImportant; + full_article.m_isDeleted = displayed_article.m_isDeleted; + full_article.m_score = displayed_article.m_score; + full_article.m_isRtl = displayed_article.m_isRtl; + full_article.m_enclosures = displayed_article.m_enclosures; + + loadMessages({full_article}, m_root); + } + else { + auto html_message = m_webView->htmlForMessages({full_article}, nullptr); + + setHtml(html_message.m_html, url); + } + + // + // m_webView->setReadabledHtml(full_article.m_contents, m_webView->url()); } } diff --git a/src/librssguard/gui/webbrowser.h b/src/librssguard/gui/webbrowser.h index 9ede837e5..8b36bc6c2 100644 --- a/src/librssguard/gui/webbrowser.h +++ b/src/librssguard/gui/webbrowser.h @@ -79,7 +79,7 @@ class RSSGUARD_DLLSPEC WebBrowser : public TabContent { void setReadabledHtml(QObject* sndr, const QString& better_html); void readabilityFailed(QObject* sndr, const QString& error); - void setFullArticleHtml(QObject* sndr, const QString& json_answer); + void setFullArticleHtml(QObject* sndr, const QString &url, const QString& json_answer); void fullArticleFailed(QObject* sndr, const QString& error); signals: @@ -92,6 +92,8 @@ class RSSGUARD_DLLSPEC WebBrowser : public TabContent { void bindWebView(); void createConnections(); + Message messageFromExtractor(const QJsonDocument& extracted_data) const; + private: QVBoxLayout* m_layout; QToolBar* m_toolBar; diff --git a/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.cpp b/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.cpp index 89a3a4dcf..5a5e7e003 100644 --- a/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.cpp +++ b/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.cpp @@ -249,15 +249,7 @@ void TextBrowserViewer::loadMessages(const QList& messages, RootItem* r emit loadingStarted(); m_root = root; - auto html_messages = - qApp->settings()->value(GROUP(Messages), SETTING(Messages::UseLegacyArticleFormat)).toBool() - ? prepareLegacyHtmlForMessage(messages, root) - : qApp->skins()->generateHtmlOfArticles(messages, root, width() * ACCEPTABLE_IMAGE_PERCENTUAL_WIDTH); - - // Remove other characters which cannot be displayed properly. - static QRegularExpression exp_symbols("[0-9A-F]{3};"); - - html_messages.m_html = html_messages.m_html.replace(exp_symbols, QString()); + auto html_messages = htmlForMessages(messages, root); /* // Replace base64 images. @@ -283,6 +275,20 @@ void TextBrowserViewer::loadMessages(const QList& messages, RootItem* r emit loadingFinished(true); } +PreparedHtml TextBrowserViewer::htmlForMessages(const QList& messages, RootItem* root) const { + auto html_messages = + qApp->settings()->value(GROUP(Messages), SETTING(Messages::UseLegacyArticleFormat)).toBool() + ? prepareLegacyHtmlForMessage(messages, root) + : qApp->skins()->generateHtmlOfArticles(messages, root, width() * ACCEPTABLE_IMAGE_PERCENTUAL_WIDTH); + + // Remove other characters which cannot be displayed properly. + static QRegularExpression exp_symbols("[0-9A-F]{3};"); + + html_messages.m_html = html_messages.m_html.replace(exp_symbols, QString()); + + return html_messages; +} + double TextBrowserViewer::verticalScrollBarPosition() const { return verticalScrollBar()->value(); } diff --git a/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.h b/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.h index f74d30449..a9339e63a 100644 --- a/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.h +++ b/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.h @@ -52,6 +52,7 @@ class RSSGUARD_DLLSPEC TextBrowserViewer : public QTextBrowser, public WebViewer virtual QUrl url() const; virtual void clear(); virtual void loadMessages(const QList& messages, RootItem* root); + virtual PreparedHtml htmlForMessages(const QList& messages, RootItem* root) const; virtual double verticalScrollBarPosition() const; virtual void setVerticalScrollBarPosition(double pos); virtual void applyFont(const QFont& fon); diff --git a/src/librssguard/gui/webviewers/webengine/webengineviewer.cpp b/src/librssguard/gui/webviewers/webengine/webengineviewer.cpp index 962fb0922..0c15d7a26 100644 --- a/src/librssguard/gui/webviewers/webengine/webengineviewer.cpp +++ b/src/librssguard/gui/webviewers/webengine/webengineviewer.cpp @@ -53,8 +53,7 @@ WebEnginePage* WebEngineViewer::page() const { } void WebEngineViewer::loadMessages(const QList& messages, RootItem* root) { - auto html_messages = - qApp->skins()->generateHtmlOfArticles(messages, root, width() * ACCEPTABLE_IMAGE_PERCENTUAL_WIDTH); + auto html_messages = htmlForMessages(messages, root); m_root = root; m_messageContents = html_messages.m_html; @@ -69,6 +68,10 @@ void WebEngineViewer::loadMessages(const QList& messages, RootItem* roo page()->runJavaScript(QSL("window.scrollTo(0, 0);")); } +PreparedHtml WebEngineViewer::htmlForMessages(const QList& messages, RootItem* root) const { + return qApp->skins()->generateHtmlOfArticles(messages, root, width() * ACCEPTABLE_IMAGE_PERCENTUAL_WIDTH); +} + void WebEngineViewer::clear() { bool previously_enabled = isEnabled(); diff --git a/src/librssguard/gui/webviewers/webengine/webengineviewer.h b/src/librssguard/gui/webviewers/webengine/webengineviewer.h index 233a7a2a7..d2c2b57c1 100644 --- a/src/librssguard/gui/webviewers/webengine/webengineviewer.h +++ b/src/librssguard/gui/webviewers/webengine/webengineviewer.h @@ -22,6 +22,7 @@ class RSSGUARD_DLLSPEC WebEngineViewer : public QWebEngineView, public WebViewer public: virtual void loadMessages(const QList& messages, RootItem* root); + virtual PreparedHtml htmlForMessages(const QList& messages, RootItem* root) const; virtual void bindToBrowser(WebBrowser* browser); virtual void findText(const QString& text, bool backwards); virtual void setUrl(const QUrl& url); diff --git a/src/librssguard/gui/webviewers/webviewer.h b/src/librssguard/gui/webviewers/webviewer.h index d8f0e84c8..d6acf5844 100644 --- a/src/librssguard/gui/webviewers/webviewer.h +++ b/src/librssguard/gui/webviewers/webviewer.h @@ -66,6 +66,9 @@ class WebViewer { // Displays all messages and ensures that vertical scrollbar is set to 0 (scrolled to top). virtual void loadMessages(const QList& messages, RootItem* root) = 0; + // Returns final HTML generated for the articles. + virtual PreparedHtml htmlForMessages(const QList& messages, RootItem* root) const = 0; + // Vertical scrollbar changer. virtual double verticalScrollBarPosition() const = 0; virtual void setVerticalScrollBarPosition(double pos) = 0; diff --git a/src/librssguard/network-web/articleparse.cpp b/src/librssguard/network-web/articleparse.cpp index 13996f41d..76f4f8011 100644 --- a/src/librssguard/network-web/articleparse.cpp +++ b/src/librssguard/network-web/articleparse.cpp @@ -40,7 +40,7 @@ void ArticleParse::onPackageReady(const QList& pkgs, bo {true, true, false}); // Emit this just to allow the action again for user. - emit articleParsed(nullptr, tr("Packages for article-extractor are installed. You can now use this feature!")); + emit articleParsed(nullptr, {}, tr("Packages for article-extractor are installed. You can now use this feature!")); } void ArticleParse::onPackageError(const QList& pkgs, const QString& error) { @@ -61,7 +61,9 @@ void ArticleParse::onPackageError(const QList& pkgs, co {true, true, false}); // Emit this just to allow readability again for user. - emit articleParsed(nullptr, tr("Packages for article-extractor are NOT installed. There is error: %1").arg(error)); + emit articleParsed(nullptr, + {}, + tr("Packages for article-extractor are NOT installed. There is error: %1").arg(error)); } void ArticleParse::parseArticle(QObject* sndr, const QString& url) { @@ -113,8 +115,10 @@ void ArticleParse::parseArticle(QObject* sndr, const QString& url) { // Emit this just to allow readability again for user. emit articleParsed(sndr, + url, tr("Node.js is not configured properly. Go to \"Settings\" -> \"Node.js\" and check " "if your Node.js is properly configured.")); + return; } } @@ -124,17 +128,20 @@ void ArticleParse::parseArticle(QObject* sndr, const QString& url) { QOverload::of(&QProcess::finished), this, [=](int exit_code, QProcess::ExitStatus exit_status) { - onParsingFinished(sndr, exit_code, exit_status); + onParsingFinished(sndr, url, exit_code, exit_status); }); qApp->nodejs()->runScript(proc, m_scriptFilename, {url}); } -void ArticleParse::onParsingFinished(QObject* sndr, int exit_code, QProcess::ExitStatus exit_status) { +void ArticleParse::onParsingFinished(QObject* sndr, + const QString& url, + int exit_code, + QProcess::ExitStatus exit_status) { QProcess* proc = qobject_cast(sender()); if (exit_status == QProcess::ExitStatus::NormalExit && exit_code == EXIT_SUCCESS) { - emit articleParsed(sndr, QString::fromUtf8(proc->readAllStandardOutput())); + emit articleParsed(sndr, url, QString::fromUtf8(proc->readAllStandardOutput())); } else { QString err = QString::fromUtf8(proc->readAllStandardError()); diff --git a/src/librssguard/network-web/articleparse.h b/src/librssguard/network-web/articleparse.h index 672273593..bea30769d 100644 --- a/src/librssguard/network-web/articleparse.h +++ b/src/librssguard/network-web/articleparse.h @@ -17,12 +17,12 @@ class ArticleParse : public QObject { void parseArticle(QObject* sndr, const QString& url); private slots: - void onParsingFinished(QObject* sndr, int exit_code, QProcess::ExitStatus exit_status); + void onParsingFinished(QObject* sndr, const QString& url, int exit_code, QProcess::ExitStatus exit_status); void onPackageReady(const QList& pkgs, bool already_up_to_date); void onPackageError(const QList& pkgs, const QString& error); signals: - void articleParsed(QObject* sndr, const QString& better_html); + void articleParsed(QObject* sndr, const QString& url, const QString& better_html); void errorOnArticlePArsing(QObject* sndr, const QString& error); private: