work on toasts, unify article sanitization

This commit is contained in:
Martin Rotter 2023-09-22 07:24:55 +02:00
parent 69dc7e0a32
commit 46fe50df91
33 changed files with 470 additions and 142 deletions

View File

@ -8,6 +8,8 @@
<file>./graphics/Breeze/categories/32/applications-system.svg</file> <file>./graphics/Breeze/categories/32/applications-system.svg</file>
<file>./graphics/Breeze/actions/32/arrow-down.svg</file> <file>./graphics/Breeze/actions/32/arrow-down.svg</file>
<file>./graphics/Breeze/actions/32/arrow-down-double.svg</file> <file>./graphics/Breeze/actions/32/arrow-down-double.svg</file>
<file>./graphics/Breeze/actions/32/arrow-left.svg</file>
<file>./graphics/Breeze/actions/32/arrow-right.svg</file>
<file>./graphics/Breeze/actions/32/arrow-up.svg</file> <file>./graphics/Breeze/actions/32/arrow-up.svg</file>
<file>./graphics/Breeze/actions/32/arrow-up-double.svg</file> <file>./graphics/Breeze/actions/32/arrow-up-double.svg</file>
<file>./graphics/Breeze/actions/32/call-start.svg</file> <file>./graphics/Breeze/actions/32/call-start.svg</file>
@ -99,6 +101,8 @@
<file>./graphics/Breeze Dark/categories/32/applications-system.svg</file> <file>./graphics/Breeze Dark/categories/32/applications-system.svg</file>
<file>./graphics/Breeze Dark/actions/32/arrow-down.svg</file> <file>./graphics/Breeze Dark/actions/32/arrow-down.svg</file>
<file>./graphics/Breeze Dark/actions/32/arrow-down-double.svg</file> <file>./graphics/Breeze Dark/actions/32/arrow-down-double.svg</file>
<file>./graphics/Breeze Dark/actions/32/arrow-left.svg</file>
<file>./graphics/Breeze Dark/actions/32/arrow-right.svg</file>
<file>./graphics/Breeze Dark/actions/32/arrow-up.svg</file> <file>./graphics/Breeze Dark/actions/32/arrow-up.svg</file>
<file>./graphics/Breeze Dark/actions/32/arrow-up-double.svg</file> <file>./graphics/Breeze Dark/actions/32/arrow-up-double.svg</file>
<file>./graphics/Breeze Dark/actions/32/call-start.svg</file> <file>./graphics/Breeze Dark/actions/32/call-start.svg</file>
@ -274,6 +278,8 @@
<file>./graphics/Numix/22/categories/applications-system.svg</file> <file>./graphics/Numix/22/categories/applications-system.svg</file>
<file>./graphics/Numix/22/actions/arrow-down.svg</file> <file>./graphics/Numix/22/actions/arrow-down.svg</file>
<file>./graphics/Numix/22/actions/arrow-down-double.svg</file> <file>./graphics/Numix/22/actions/arrow-down-double.svg</file>
<file>./graphics/Numix/22/actions/arrow-left.svg</file>
<file>./graphics/Numix/22/actions/arrow-right.svg</file>
<file>./graphics/Numix/22/actions/arrow-up.svg</file> <file>./graphics/Numix/22/actions/arrow-up.svg</file>
<file>./graphics/Numix/22/actions/arrow-up-double.svg</file> <file>./graphics/Numix/22/actions/arrow-up-double.svg</file>
<file>./graphics/Numix/22/actions/browser-download.svg</file> <file>./graphics/Numix/22/actions/browser-download.svg</file>

View File

@ -1,4 +1,6 @@
set(SOURCES set(SOURCES
core/articlelistnotificationmodel.cpp
core/articlelistnotificationmodel.h
core/feeddownloader.cpp core/feeddownloader.cpp
core/feeddownloader.h core/feeddownloader.h
core/feedsmodel.cpp core/feedsmodel.cpp

View File

@ -0,0 +1,23 @@
// For license of this file, see <project-root-folder>/LICENSE.md.
#include "core/articlelistnotificationmodel.h"
ArticleListNotificationModel::ArticleListNotificationModel(QObject* parent)
: QAbstractListModel(parent), m_currentPage(-1) {}
ArticleListNotificationModel::~ArticleListNotificationModel() {}
void ArticleListNotificationModel::setArticles(const QList<Message>& msgs) {
m_articles = msgs;
m_currentPage = 0;
}
void ArticleListNotificationModel::nextPage() {}
void ArticleListNotificationModel::previousPage() {}
int ArticleListNotificationModel::rowCount(const QModelIndex& parent) const {}
int ArticleListNotificationModel::columnCount(const QModelIndex& parent) const {}
QVariant ArticleListNotificationModel::data(const QModelIndex& index, int role) const {}

View File

@ -0,0 +1,33 @@
// For license of this file, see <project-root-folder>/LICENSE.md.
#ifndef ARTICLELISTNOTIFICATIONMODEL_H
#define ARTICLELISTNOTIFICATIONMODEL_H
#include <QAbstractListModel>
#include "core/message.h"
class ArticleListNotificationModel : public QAbstractListModel {
public:
explicit ArticleListNotificationModel(QObject* parent = nullptr);
virtual ~ArticleListNotificationModel();
void setArticles(const QList<Message>& msgs);
void nextPage();
void previousPage();
virtual int rowCount(const QModelIndex& parent) const;
virtual int columnCount(const QModelIndex& parent) const;
virtual QVariant data(const QModelIndex& index, int role) const;
signals:
void nextPagePossibleChanged(bool possible);
void previousPagePossibleChanged(bool possible);
private:
QList<Message> m_articles;
int m_currentPage;
};
#endif // ARTICLELISTNOTIFICATIONMODEL_H

View File

@ -413,15 +413,17 @@ void FeedDownloader::updateOneFeed(ServiceRoot* acc,
<< "microseconds."; << "microseconds.";
if (feed->status() != Feed::Status::NewMessages) { if (feed->status() != Feed::Status::NewMessages) {
feed->setStatus(updated_messages.first > 0 || updated_messages.second > 0 ? Feed::Status::NewMessages feed->setStatus((!updated_messages.m_all.isEmpty() || !updated_messages.m_unread.isEmpty())
: Feed::Status::Normal); ? Feed::Status::NewMessages
: Feed::Status::Normal);
} }
qDebugNN << LOGSEC_FEEDDOWNLOADER << updated_messages << " messages for feed " << feed->customId() qDebugNN << LOGSEC_FEEDDOWNLOADER << updated_messages.m_unread.size() << " unread messages and"
<< " stored in DB."; << NONQUOTE_W_SPACE(updated_messages.m_all.size()) "total messages for feed"
<< QUOTE_W_SPACE(feed->customId()) << "stored in DB.";
if (updated_messages.first > 0) { if (!updated_messages.m_unread.isEmpty()) {
m_results.appendUpdatedFeed({feed, updated_messages.first}); m_results.appendUpdatedFeed(feed, updated_messages.m_unread);
} }
} }
catch (const FeedFetchException& feed_ex) { catch (const FeedFetchException& feed_ex) {
@ -445,7 +447,6 @@ void FeedDownloader::finalizeUpdate() {
qDebugNN << LOGSEC_FEEDDOWNLOADER << "Finished feed updates in thread" qDebugNN << LOGSEC_FEEDDOWNLOADER << "Finished feed updates in thread"
<< QUOTE_W_SPACE_DOT(QThread::currentThreadId()); << QUOTE_W_SPACE_DOT(QThread::currentThreadId());
m_results.sort();
m_feeds.clear(); m_feeds.clear();
// Update of feeds has finished. // Update of feeds has finished.
@ -528,7 +529,8 @@ QString FeedDownloadResults::overview(int how_many_feeds) const {
QStringList result; QStringList result;
for (int i = 0, number_items_output = qMin(how_many_feeds, m_updatedFeeds.size()); i < number_items_output; i++) { for (int i = 0, number_items_output = qMin(how_many_feeds, m_updatedFeeds.size()); i < number_items_output; i++) {
result.append(m_updatedFeeds.at(i).first->title() + QSL(": ") + QString::number(m_updatedFeeds.at(i).second)); result.append(m_updatedFeeds.keys().at(i)->title() + QSL(": ") +
QString::number(m_updatedFeeds.value(m_updatedFeeds.keys().at(i)).size()));
} }
QString res_str = result.join(QSL("\n")); QString res_str = result.join(QSL("\n"));
@ -540,22 +542,14 @@ QString FeedDownloadResults::overview(int how_many_feeds) const {
return res_str; return res_str;
} }
void FeedDownloadResults::appendUpdatedFeed(const QPair<Feed*, int>& feed) { void FeedDownloadResults::appendUpdatedFeed(Feed* feed, const QList<Message>& updated_unread_msgs) {
m_updatedFeeds.append(feed); m_updatedFeeds.insert(feed, updated_unread_msgs);
}
void FeedDownloadResults::sort() {
std::sort(m_updatedFeeds.begin(),
m_updatedFeeds.end(),
[](const QPair<Feed*, int>& lhs, const QPair<Feed*, int>& rhs) {
return lhs.second > rhs.second;
});
} }
void FeedDownloadResults::clear() { void FeedDownloadResults::clear() {
m_updatedFeeds.clear(); m_updatedFeeds.clear();
} }
QList<QPair<Feed*, int>> FeedDownloadResults::updatedFeeds() const { QHash<Feed*, QList<Message>> FeedDownloadResults::updatedFeeds() const {
return m_updatedFeeds; return m_updatedFeeds;
} }

View File

@ -5,29 +5,28 @@
#include <QObject> #include <QObject>
#include <QFutureWatcher>
#include <QPair>
#include "core/message.h" #include "core/message.h"
#include "exceptions/applicationexception.h" #include "exceptions/applicationexception.h"
#include "services/abstract/cacheforserviceroot.h" #include "services/abstract/cacheforserviceroot.h"
#include "services/abstract/feed.h" #include "services/abstract/feed.h"
#include <QFutureWatcher>
#include <QHash>
#include <QPair>
class MessageFilter; class MessageFilter;
// Represents results of batch feed updates. // Represents results of batch feed updates.
class FeedDownloadResults { class FeedDownloadResults {
public: public:
QList<QPair<Feed*, int>> updatedFeeds() const; QHash<Feed*, QList<Message>> updatedFeeds() const;
QString overview(int how_many_feeds) const; QString overview(int how_many_feeds) const;
void appendUpdatedFeed(Feed* feed, const QList<Message>& updated_unread_msgs);
void appendUpdatedFeed(const QPair<Feed*, int>& feed);
void sort();
void clear(); void clear();
private: private:
// QString represents title if the feed, int represents count of newly downloaded messages. // QString represents title if the feed, int represents count of newly downloaded messages.
QList<QPair<Feed*, int>> m_updatedFeeds; QHash<Feed*, QList<Message>> m_updatedFeeds;
}; };
struct FeedUpdateRequest { struct FeedUpdateRequest {

View File

@ -2,7 +2,9 @@
#include "core/message.h" #include "core/message.h"
#include "miscellaneous/application.h"
#include "miscellaneous/textfactory.h" #include "miscellaneous/textfactory.h"
#include "network-web/webfactory.h"
#include "services/abstract/feed.h" #include "services/abstract/feed.h"
#include "services/abstract/label.h" #include "services/abstract/label.h"
@ -75,6 +77,8 @@ Message::Message() {
void Message::sanitize(const Feed* feed, bool fix_future_datetimes) { void Message::sanitize(const Feed* feed, bool fix_future_datetimes) {
// Sanitize title. // Sanitize title.
m_title = qApp->web()->stripTags(qApp->web()->unescapeHtml(m_title));
m_title = m_title m_title = m_title
// Remove non-breaking spaces. // Remove non-breaking spaces.
@ -89,6 +93,9 @@ void Message::sanitize(const Feed* feed, bool fix_future_datetimes) {
// Remove non-breaking zero-width spaces. // Remove non-breaking zero-width spaces.
.remove(QChar(65279)); .remove(QChar(65279));
// Sanitize author.
m_author = qApp->web()->stripTags(qApp->web()->unescapeHtml(m_author));
// Sanitize URL. // Sanitize URL.
m_url = m_url.trimmed(); m_url = m_url.trimmed();

View File

@ -1354,7 +1354,7 @@ QHash<QString, QStringList> DatabaseQueries::bagsOfMessages(const QSqlDatabase&
return ids; return ids;
} }
QPair<int, int> DatabaseQueries::updateMessages(const QSqlDatabase& db, UpdatedArticles DatabaseQueries::updateMessages(const QSqlDatabase& db,
QList<Message>& messages, QList<Message>& messages,
Feed* feed, Feed* feed,
bool force_update, bool force_update,
@ -1362,10 +1362,10 @@ QPair<int, int> DatabaseQueries::updateMessages(const QSqlDatabase& db,
bool* ok) { bool* ok) {
if (messages.isEmpty()) { if (messages.isEmpty()) {
*ok = true; *ok = true;
return {0, 0}; return {};
} }
QPair<int, int> updated_messages = {0, 0}; UpdatedArticles updated_messages;
int account_id = feed->getParentServiceRoot()->accountId(); int account_id = feed->getParentServiceRoot()->accountId();
auto feed_custom_id = feed->customId(); auto feed_custom_id = feed->customId();
@ -1615,10 +1615,10 @@ QPair<int, int> DatabaseQueries::updateMessages(const QSqlDatabase& db,
<< QUOTE_W_SPACE(message.m_url) << "in DB."; << QUOTE_W_SPACE(message.m_url) << "in DB.";
if (!message.m_isRead) { if (!message.m_isRead) {
updated_messages.first++; updated_messages.m_unread.append(message);
} }
updated_messages.second++; updated_messages.m_all.append(message);
message.m_insertedUpdated = true; message.m_insertedUpdated = true;
} }
else if (query_update.lastError().isValid()) { else if (query_update.lastError().isValid()) {
@ -1655,10 +1655,10 @@ QPair<int, int> DatabaseQueries::updateMessages(const QSqlDatabase& db,
} }
if (!msg->m_isRead) { if (!msg->m_isRead) {
updated_messages.first++; updated_messages.m_unread.append(*msg);
} }
updated_messages.second++; updated_messages.m_all.append(*msg);
msg->m_insertedUpdated = true; msg->m_insertedUpdated = true;
vals.append(QSL("\n(':feed', ':title', :is_read, :is_important, :is_deleted, " vals.append(QSL("\n(':feed', ':title', :is_read, :is_important, :is_deleted, "
@ -1742,10 +1742,10 @@ QPair<int, int> DatabaseQueries::updateMessages(const QSqlDatabase& db,
// but its assigned labels were changed. Therefore we must count article // but its assigned labels were changed. Therefore we must count article
// as updated. // as updated.
if (!message.m_isRead) { if (!message.m_isRead) {
updated_messages.first++; updated_messages.m_unread.append(message);
} }
updated_messages.second++; updated_messages.m_all.append(message);
} }
} }
else { else {

View File

@ -21,11 +21,6 @@
#include <QSqlError> #include <QSqlError>
#include <QSqlQuery> #include <QSqlQuery>
struct ArticleCounts {
int m_total = -1;
int m_unread = -1;
};
class DatabaseQueries { class DatabaseQueries {
public: public:
static QMap<int, QString> messageTableAttributes(bool only_msg_table, bool is_sqlite); static QMap<int, QString> messageTableAttributes(bool only_msg_table, bool is_sqlite);
@ -162,7 +157,7 @@ class DatabaseQueries {
static void createOverwriteAccount(const QSqlDatabase& db, ServiceRoot* account); static void createOverwriteAccount(const QSqlDatabase& db, ServiceRoot* account);
// Returns counts of updated messages <unread, all>. // Returns counts of updated messages <unread, all>.
static QPair<int, int> updateMessages(const QSqlDatabase& db, static UpdatedArticles updateMessages(const QSqlDatabase& db,
QList<Message>& messages, QList<Message>& messages,
Feed* feed, Feed* feed,
bool force_update, bool force_update,

View File

@ -100,9 +100,10 @@
#define MAX_THREADPOOL_THREADS 32 #define MAX_THREADPOOL_THREADS 32
#define WEB_BROWSER_SCROLL_STEP 50.0 #define WEB_BROWSER_SCROLL_STEP 50.0
#define NOTIFICATIONS_MARGIN 16 #define NOTIFICATIONS_MARGIN 16
#define NOTIFICATIONS_WIDTH 256 #define NOTIFICATIONS_WIDTH 256
#define NOTIFICATIONS_TIMEOUT 15s #define NOTIFICATIONS_TIMEOUT 15s
#define NOTIFICATIONS_PAGE_SIZE 10
#define GOOGLE_SEARCH_URL "https://www.google.com/search?q=%1&ie=utf-8&oe=utf-8" #define GOOGLE_SEARCH_URL "https://www.google.com/search?q=%1&ie=utf-8&oe=utf-8"
#define GOOGLE_SUGGEST_URL "http://suggestqueries.google.com/complete/search?output=toolbar&hl=en&q=%1" #define GOOGLE_SUGGEST_URL "http://suggestqueries.google.com/complete/search?output=toolbar&hl=en&q=%1"

View File

@ -14,4 +14,14 @@ typedef QList<QPair<int, RootItem*>> Assignment;
typedef QPair<int, RootItem*> AssignmentItem; typedef QPair<int, RootItem*> AssignmentItem;
typedef QPair<Message, RootItem::Importance> ImportanceChange; typedef QPair<Message, RootItem::Importance> ImportanceChange;
struct ArticleCounts {
int m_total = -1;
int m_unread = -1;
};
struct UpdatedArticles {
QList<Message> m_unread;
QList<Message> m_all;
};
#endif // TYPEDEFS_H #endif // TYPEDEFS_H

View File

@ -2,9 +2,21 @@
#include "gui/notifications/articlelistnotification.h" #include "gui/notifications/articlelistnotification.h"
#include "miscellaneous/iconfactory.h"
ArticleListNotification::ArticleListNotification(QWidget* parent) : BaseToastNotification(parent) { ArticleListNotification::ArticleListNotification(QWidget* parent) : BaseToastNotification(parent) {
m_ui.setupUi(this); m_ui.setupUi(this);
setupCloseButton(m_ui.m_btnClose); setupCloseButton(m_ui.m_btnClose);
setupTimedClosing();
m_ui.m_btnNextPage->setIcon(qApp->icons()->fromTheme(QSL("arrow-right"), QSL("stock_right")));
m_ui.m_btnPreviousPage->setIcon(qApp->icons()->fromTheme(QSL("arrow-left"), QSL("stock_left")));
m_ui.m_btnOpenArticleList->setIcon(qApp->icons()->fromTheme(QSL("view-list-details")));
m_ui.m_btnOpenWebBrowser->setIcon(qApp->icons()->fromTheme(QSL("document-open")));
}
void ArticleListNotification::loadResults(const QHash<Feed*, QList<Message>>& new_messages) {
setupTimedClosing();
m_ui.m_treeArticles->setModel()
} }

View File

@ -5,14 +5,20 @@
#include "gui/notifications/basetoastnotification.h" #include "gui/notifications/basetoastnotification.h"
#include "core/message.h"
#include "ui_articlelistnotification.h" #include "ui_articlelistnotification.h"
class Feed;
class ArticleListNotification : public BaseToastNotification { class ArticleListNotification : public BaseToastNotification {
Q_OBJECT Q_OBJECT
public: public:
explicit ArticleListNotification(QWidget* parent = nullptr); explicit ArticleListNotification(QWidget* parent = nullptr);
void loadResults(const QHash<Feed*, QList<Message>>& new_messages);
private: private:
Ui::ArticleListNotification m_ui; Ui::ArticleListNotification m_ui;
}; };

View File

@ -0,0 +1,130 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ArticleListNotification</class>
<widget class="QDialog" name="ArticleListNotification">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>338</width>
<height>271</height>
</rect>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item row="1" column="0" colspan="2">
<widget class="QTreeView" name="m_treeArticles">
<property name="uniformRowHeights">
<bool>true</bool>
</property>
<property name="itemsExpandable">
<bool>false</bool>
</property>
<property name="expandsOnDoubleClick">
<bool>false</bool>
</property>
<attribute name="headerVisible">
<bool>true</bool>
</attribute>
</widget>
</item>
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="m_titleLayout">
<item>
<widget class="QLabel" name="m_lblTitle">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="PlainToolButton" name="m_btnClose">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="PlainToolButton" name="m_btnPreviousPage">
<property name="toolTip">
<string>Go to previous page</string>
</property>
</widget>
</item>
<item>
<widget class="PlainToolButton" name="m_btnNextPage">
<property name="toolTip">
<string>Go to next page</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="PlainToolButton" name="m_btnOpenArticleList">
<property name="toolTip">
<string>Open article in article list</string>
</property>
</widget>
</item>
<item>
<widget class="PlainToolButton" name="m_btnOpenWebBrowser">
<property name="toolTip">
<string>Open article in web browser</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>PlainToolButton</class>
<extends>QToolButton</extends>
<header>plaintoolbutton.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>m_btnClose</tabstop>
<tabstop>m_treeArticles</tabstop>
<tabstop>m_btnPreviousPage</tabstop>
<tabstop>m_btnNextPage</tabstop>
<tabstop>m_btnOpenArticleList</tabstop>
<tabstop>m_btnOpenWebBrowser</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@ -6,12 +6,13 @@
#include <QCloseEvent> #include <QCloseEvent>
#include <QTimer> #include <QTimer>
#include <QTimerEvent>
#include <chrono> #include <chrono>
using namespace std::chrono_literals; using namespace std::chrono_literals;
BaseToastNotification::BaseToastNotification(QWidget* parent) : QDialog(parent) { BaseToastNotification::BaseToastNotification(QWidget* parent) : QDialog(parent), m_timerId(-1) {
setAttribute(Qt::WidgetAttribute::WA_ShowWithoutActivating); setAttribute(Qt::WidgetAttribute::WA_ShowWithoutActivating);
setFixedWidth(NOTIFICATIONS_WIDTH); setFixedWidth(NOTIFICATIONS_WIDTH);
setFocusPolicy(Qt::FocusPolicy::NoFocus); setFocusPolicy(Qt::FocusPolicy::NoFocus);
@ -39,8 +40,15 @@ void BaseToastNotification::setupCloseButton(QAbstractButton* btn) {
connect(btn, &QAbstractButton::clicked, this, &BaseToastNotification::close); connect(btn, &QAbstractButton::clicked, this, &BaseToastNotification::close);
} }
void BaseToastNotification::stopTimedClosing() {
killTimer(m_timerId);
m_timerId = -1;
}
void BaseToastNotification::setupTimedClosing() { void BaseToastNotification::setupTimedClosing() {
QTimer::singleShot(NOTIFICATIONS_TIMEOUT, this, &BaseToastNotification::close); if (m_timerId < 0) {
m_timerId = startTimer(NOTIFICATIONS_TIMEOUT);
}
} }
bool BaseToastNotification::eventFilter(QObject* watched, QEvent* event) { bool BaseToastNotification::eventFilter(QObject* watched, QEvent* event) {
@ -48,12 +56,28 @@ bool BaseToastNotification::eventFilter(QObject* watched, QEvent* event) {
return true; return true;
} }
else { else {
if (event->type() == QEvent::Type::Enter) {
stopTimedClosing();
}
if (event->type() == QEvent::Type::Leave) {
setupTimedClosing();
}
return QDialog::eventFilter(watched, event); return QDialog::eventFilter(watched, event);
} }
} }
void BaseToastNotification::closeEvent(QCloseEvent* event) { void BaseToastNotification::closeEvent(QCloseEvent* event) {
stopTimedClosing();
emit closeRequested(this); emit closeRequested(this);
} }
void BaseToastNotification::reject() {} void BaseToastNotification::reject() {}
void BaseToastNotification::timerEvent(QTimerEvent* event) {
if (event->timerId() == m_timerId) {
stopTimedClosing();
emit closeRequested(this);
}
}

View File

@ -19,10 +19,12 @@ class BaseToastNotification : public QDialog {
protected: protected:
virtual bool eventFilter(QObject* watched, QEvent* event); virtual bool eventFilter(QObject* watched, QEvent* event);
virtual void timerEvent(QTimerEvent* event);
virtual void closeEvent(QCloseEvent* event); virtual void closeEvent(QCloseEvent* event);
void setupCloseButton(QAbstractButton* btn);
void setupTimedClosing(); void setupTimedClosing();
void setupCloseButton(QAbstractButton* btn);
void stopTimedClosing();
signals: signals:
void closeRequested(BaseToastNotification* notif); void closeRequested(BaseToastNotification* notif);

View File

@ -43,7 +43,10 @@ void ToastNotification::loadNotification(Notification::Event event, const GuiMes
if (action.m_action) { if (action.m_action) {
m_ui.m_btnAction->setText(action.m_title.isEmpty() ? tr("Do it!") : action.m_title); m_ui.m_btnAction->setText(action.m_title.isEmpty() ? tr("Do it!") : action.m_title);
connect(m_ui.m_btnAction, &QPushButton::clicked, this, action.m_action); connect(m_ui.m_btnAction, &QPushButton::clicked, this, [this, action]() {
action.m_action();
emit closeRequested(this);
});
} }
else { else {
m_ui.m_mainLayout->removeItem(m_ui.m_actionLayout); m_ui.m_mainLayout->removeItem(m_ui.m_actionLayout);

View File

@ -10,9 +10,6 @@
<height>143</height> <height>143</height>
</rect> </rect>
</property> </property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QFormLayout" name="m_mainLayout"> <layout class="QFormLayout" name="m_mainLayout">
<property name="leftMargin"> <property name="leftMargin">
<number>6</number> <number>6</number>

View File

@ -12,6 +12,23 @@
#include <QScreen> #include <QScreen>
#include <QWindow> #include <QWindow>
QString ToastNotificationsManager::textForPosition(ToastNotificationsManager::NotificationPosition pos) {
switch (pos) {
case TopLeft:
return QObject::tr("top-left");
case TopRight:
return QObject::tr("top-right");
case BottomLeft:
return QObject::tr("bottom-left");
case BottomRight:
default:
return QObject::tr("bottom-right");
}
}
ToastNotificationsManager::ToastNotificationsManager(NotificationPosition position, int screen, QObject* parent) ToastNotificationsManager::ToastNotificationsManager(NotificationPosition position, int screen, QObject* parent)
: QObject(parent), m_position(position), m_screen(screen), m_articleListNotification(nullptr) {} : QObject(parent), m_position(position), m_screen(screen), m_articleListNotification(nullptr) {}
@ -50,9 +67,26 @@ void ToastNotificationsManager::clear() {
void ToastNotificationsManager::showNotification(Notification::Event event, void ToastNotificationsManager::showNotification(Notification::Event event,
const GuiMessage& msg, const GuiMessage& msg,
const GuiAction& action) { const GuiAction& action) {
ToastNotification* notif = new ToastNotification(event, msg, action, qApp->mainFormWidget()); BaseToastNotification* notif;
hookNotification(notif); if (!msg.m_feedFetchResults.updatedFeeds().isEmpty()) {
if (m_articleListNotification == nullptr) {
m_articleListNotification = new ArticleListNotification();
hookNotification(m_articleListNotification);
}
else if (m_activeNotifications.contains(m_articleListNotification)) {
// Article notification is somewhere in list, clear first to move it to first positon.
closeNotification(m_articleListNotification, false);
}
m_articleListNotification->loadResults(msg.m_feedFetchResults.updatedFeeds());
notif = m_articleListNotification;
}
else {
notif = new ToastNotification(event, msg, action, qApp->mainFormWidget());
hookNotification(notif);
}
auto* screen = moveToProperScreen(notif); auto* screen = moveToProperScreen(notif);
@ -76,39 +110,6 @@ void ToastNotificationsManager::showNotification(Notification::Event event,
m_activeNotifications.prepend(notif); m_activeNotifications.prepend(notif);
} }
void ToastNotificationsManager::showNotification(const QList<Message>& new_messages) {
if (m_articleListNotification == nullptr) {
m_articleListNotification = new ArticleListNotification();
hookNotification(m_articleListNotification);
}
if (!m_activeNotifications.isEmpty() && m_activeNotifications.first() != m_articleListNotification) {
// Article notification is somewhere in list, clear first to move it to first positon.
closeNotification(m_articleListNotification, false);
}
auto* screen = moveToProperScreen(m_articleListNotification);
// Insert new notification into free space.
m_articleListNotification->show();
auto notif_new_pos = cornerForNewNotification(screen->availableGeometry());
// Make sure notification is finally resized.
m_articleListNotification->adjustSize();
qApp->processEvents();
// Move notification, at this point we already need to know its precise size.
moveNotificationToCorner(m_articleListNotification, notif_new_pos);
// Remove out-of-bounds old notifications and shift existing
// ones to make space for new notifications.
removeOutOfBoundsNotifications(m_articleListNotification->height());
makeSpaceForNotification(m_articleListNotification->height());
m_activeNotifications.prepend(m_articleListNotification);
}
void ToastNotificationsManager::closeNotification(BaseToastNotification* notif, bool delete_from_memory) { void ToastNotificationsManager::closeNotification(BaseToastNotification* notif, bool delete_from_memory) {
auto notif_idx = m_activeNotifications.indexOf(notif); auto notif_idx = m_activeNotifications.indexOf(notif);
@ -160,7 +161,7 @@ QPoint ToastNotificationsManager::cornerForNewNotification(QRect screen_rect) {
void ToastNotificationsManager::hookNotification(BaseToastNotification* notif) { void ToastNotificationsManager::hookNotification(BaseToastNotification* notif) {
connect(notif, &BaseToastNotification::closeRequested, this, [this](BaseToastNotification* notif) { connect(notif, &BaseToastNotification::closeRequested, this, [this](BaseToastNotification* notif) {
closeNotification(notif, false); closeNotification(notif, notif != m_articleListNotification);
}); });
} }

View File

@ -23,6 +23,10 @@ class ToastNotificationsManager : public QObject {
BottomRight = 3 BottomRight = 3
}; };
Q_ENUM(NotificationPosition)
static QString textForPosition(ToastNotificationsManager::NotificationPosition pos);
explicit ToastNotificationsManager(ToastNotificationsManager::NotificationPosition position, explicit ToastNotificationsManager(ToastNotificationsManager::NotificationPosition position,
int screen, int screen,
QObject* parent = nullptr); QObject* parent = nullptr);
@ -41,7 +45,6 @@ class ToastNotificationsManager : public QObject {
public slots: public slots:
void clear(); void clear();
void showNotification(Notification::Event event, const GuiMessage& msg, const GuiAction& action); void showNotification(Notification::Event event, const GuiMessage& msg, const GuiAction& action);
void showNotification(const QList<Message>& new_messages);
private slots: private slots:
void closeNotification(BaseToastNotification* notif, bool delete_from_memory); void closeNotification(BaseToastNotification* notif, bool delete_from_memory);

View File

@ -26,6 +26,7 @@ HelpSpoiler::HelpSpoiler(QWidget* parent)
m_btnToggle->setCheckable(true); m_btnToggle->setCheckable(true);
m_btnToggle->setChecked(false); m_btnToggle->setChecked(false);
m_content->setStyleSheet(QSL("QScrollArea { border: 1px solid %1; }").arg(palette().windowText().color().name()));
m_content->setSizePolicy(QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Fixed); m_content->setSizePolicy(QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Fixed);
m_content->setMaximumHeight(0); m_content->setMaximumHeight(0);
m_content->setMinimumHeight(0); m_content->setMinimumHeight(0);

View File

@ -8,6 +8,8 @@
#include "miscellaneous/settings.h" #include "miscellaneous/settings.h"
#include <QDir> #include <QDir>
#include <QMetaEnum>
#include <QScreen>
SettingsNotifications::SettingsNotifications(Settings* settings, QWidget* parent) : SettingsPanel(settings, parent) { SettingsNotifications::SettingsNotifications(Settings* settings, QWidget* parent) : SettingsPanel(settings, parent) {
m_ui.setupUi(this); m_ui.setupUi(this);
@ -25,6 +27,14 @@ SettingsNotifications::SettingsNotifications(Settings* settings, QWidget* parent
connect(m_ui.m_rbNativeNotifications, &QRadioButton::toggled, this, &SettingsNotifications::dirtifySettings); connect(m_ui.m_rbNativeNotifications, &QRadioButton::toggled, this, &SettingsNotifications::dirtifySettings);
connect(m_ui.m_rbNativeNotifications, &QRadioButton::toggled, this, &SettingsNotifications::requireRestart); connect(m_ui.m_rbNativeNotifications, &QRadioButton::toggled, this, &SettingsNotifications::requireRestart);
connect(m_ui.m_sbScreen, &QSpinBox::valueChanged, this, &SettingsNotifications::dirtifySettings);
connect(m_ui.m_sbScreen, &QSpinBox::valueChanged, this, &SettingsNotifications::requireRestart);
m_ui.m_sbScreen->setMinimum(-1);
m_ui.m_sbScreen->setMaximum(QGuiApplication::screens().size() - 1);
connect(m_ui.m_sbScreen, &QSpinBox::valueChanged, this, &SettingsNotifications::showScreenInfo);
connect(m_ui.m_cbCustomNotificationsPosition, connect(m_ui.m_cbCustomNotificationsPosition,
&QComboBox::currentIndexChanged, &QComboBox::currentIndexChanged,
this, this,
@ -33,6 +43,15 @@ SettingsNotifications::SettingsNotifications(Settings* settings, QWidget* parent
&QComboBox::currentIndexChanged, &QComboBox::currentIndexChanged,
this, this,
&SettingsNotifications::requireRestart); &SettingsNotifications::requireRestart);
QMetaEnum enm = QMetaEnum::fromType<ToastNotificationsManager::NotificationPosition>();
for (int i = 0; i < enm.keyCount(); i++) {
m_ui.m_cbCustomNotificationsPosition
->addItem(ToastNotificationsManager::
textForPosition(ToastNotificationsManager::NotificationPosition(enm.value(i))),
enm.value(i));
}
} }
void SettingsNotifications::loadSettings() { void SettingsNotifications::loadSettings() {
@ -43,6 +62,16 @@ void SettingsNotifications::loadSettings() {
->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::EnableNotifications)).toBool()); ->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::EnableNotifications)).toBool());
m_ui.m_editor->loadNotifications(qApp->notifications()->allNotifications()); m_ui.m_editor->loadNotifications(qApp->notifications()->allNotifications());
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_cbCustomNotificationsPosition
->setCurrentIndex(m_ui.m_cbCustomNotificationsPosition
->findData(settings()
->value(GROUP(GUI), SETTING(GUI::ToastNotificationsPosition))
.value<ToastNotificationsManager::NotificationPosition>()));
onEndLoadSettings(); onEndLoadSettings();
} }
@ -53,5 +82,29 @@ void SettingsNotifications::saveSettings() {
settings()->setValue(GROUP(GUI), GUI::EnableNotifications, m_ui.m_checkEnableNotifications->isChecked()); settings()->setValue(GROUP(GUI), GUI::EnableNotifications, m_ui.m_checkEnableNotifications->isChecked());
qApp->notifications()->save(m_ui.m_editor->allNotifications(), settings()); qApp->notifications()->save(m_ui.m_editor->allNotifications(), settings());
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::ToastNotificationsPosition,
m_ui.m_cbCustomNotificationsPosition->currentData()
.value<ToastNotificationsManager::NotificationPosition>());
onEndSaveSettings(); onEndSaveSettings();
} }
void SettingsNotifications::showScreenInfo(int index) {
QScreen* scr;
if (index < 0 || index >= QGuiApplication::screens().size()) {
scr = QGuiApplication::primaryScreen();
}
else {
scr = QGuiApplication::screens().at(index);
}
m_ui.m_lblScreenInfo->setText(QSL("%1 (%2x%3)")
.arg(scr->name(),
QString::number(scr->virtualSize().width()),
QString::number(scr->virtualSize().height())));
}

View File

@ -10,7 +10,7 @@
class Settings; class Settings;
class SettingsNotifications : public SettingsPanel { class SettingsNotifications : public SettingsPanel {
Q_OBJECT Q_OBJECT
public: public:
explicit SettingsNotifications(Settings* settings, QWidget* parent = nullptr); explicit SettingsNotifications(Settings* settings, QWidget* parent = nullptr);
@ -19,6 +19,9 @@ class SettingsNotifications : public SettingsPanel {
virtual void loadSettings(); virtual void loadSettings();
virtual void saveSettings(); virtual void saveSettings();
private slots:
void showScreenInfo(int index);
private: private:
Ui::SettingsNotifications m_ui; Ui::SettingsNotifications m_ui;
}; };

View File

@ -45,9 +45,6 @@
<property name="text"> <property name="text">
<string>Native notifications (tray icon must be enabled)</string> <string>Native notifications (tray icon must be enabled)</string>
</property> </property>
<property name="checked">
<bool>true</bool>
</property>
</widget> </widget>
</item> </item>
<item row="1" column="0" colspan="2"> <item row="1" column="0" colspan="2">
@ -55,6 +52,9 @@
<property name="text"> <property name="text">
<string>Custom notifications</string> <string>Custom notifications</string>
</property> </property>
<property name="checked">
<bool>true</bool>
</property>
</widget> </widget>
</item> </item>
<item row="2" column="0" colspan="2"> <item row="2" column="0" colspan="2">
@ -76,6 +76,27 @@
<item row="0" column="1"> <item row="0" column="1">
<widget class="QComboBox" name="m_cbCustomNotificationsPosition"/> <widget class="QComboBox" name="m_cbCustomNotificationsPosition"/>
</item> </item>
<item row="1" column="1">
<widget class="QSpinBox" name="m_sbScreen">
<property name="value">
<number>99</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Screen</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="m_lblScreenInfo">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>

View File

@ -134,7 +134,7 @@ PreparedHtml TextBrowserViewer::prepareHtmlForMessage(const QList<Message>& mess
html.m_html += is_plain ? Qt::convertFromPlainText(message.m_contents, Qt::WhiteSpaceMode::WhiteSpaceNormal) html.m_html += is_plain ? Qt::convertFromPlainText(message.m_contents, Qt::WhiteSpaceMode::WhiteSpaceNormal)
: message.m_contents; : message.m_contents;
static QRegularExpression img_tag_rgx("\\<img[^\\>]*src\\s*=\\s*[\"\']([^\"\']*)[\"\'][^\\>]*\\>", static QRegularExpression img_tag_rgx(QSL("\\<img[^\\>]*src\\s*=\\s*[\"\']([^\"\']*)[\"\'][^\\>]*\\>"),
QRegularExpression::PatternOption::CaseInsensitiveOption | QRegularExpression::PatternOption::CaseInsensitiveOption |
QRegularExpression::PatternOption::InvertedGreedinessOption); QRegularExpression::PatternOption::InvertedGreedinessOption);

View File

@ -111,11 +111,13 @@ Application::Application(const QString& id, int& argc, char** argv, const QStrin
m_downloadManager = nullptr; m_downloadManager = nullptr;
m_notifications = new NotificationFactory(this); m_notifications = new NotificationFactory(this);
m_toastNotifications = m_toastNotifications =
new ToastNotificationsManager(settings() settings()->value(GROUP(GUI), SETTING(GUI::UseToastNotifications)).toBool()
->value(GROUP(GUI), SETTING(GUI::ToastNotificationsPosition)) ? new ToastNotificationsManager(settings()
.value<ToastNotificationsManager::NotificationPosition>(), ->value(GROUP(GUI), SETTING(GUI::ToastNotificationsPosition))
settings()->value(GROUP(GUI), SETTING(GUI::ToastNotificationsScreen)).toInt(), .value<ToastNotificationsManager::NotificationPosition>(),
this); settings()->value(GROUP(GUI), SETTING(GUI::ToastNotificationsScreen)).toInt(),
this)
: nullptr;
m_shouldRestart = false; m_shouldRestart = false;
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
@ -695,22 +697,26 @@ void Application::showGuiMessageCore(Notification::Event event,
GuiMessageDestination dest, GuiMessageDestination dest,
const GuiAction& action, const GuiAction& action,
QWidget* parent) { QWidget* parent) {
m_toastNotifications->showNotification(event, msg, action);
return;
if (m_notifications->areNotificationsEnabled()) { if (m_notifications->areNotificationsEnabled()) {
auto notification = m_notifications->notificationForEvent(event); auto notification = m_notifications->notificationForEvent(event);
notification.playSound(this); notification.playSound(this);
if (SystemTrayIcon::isSystemTrayDesired() && SystemTrayIcon::isSystemTrayAreaAvailable() && if (notification.balloonEnabled() && dest.m_tray) {
notification.balloonEnabled() && dest.m_tray) { if (m_toastNotifications != nullptr) {
trayIcon()->showMessage(msg.m_title.simplified().isEmpty() ? Notification::nameForEvent(notification.event()) // Toasts are enabled.
: msg.m_title, m_toastNotifications->showNotification(event, msg, action);
msg.m_message, }
msg.m_type, else if (SystemTrayIcon::isSystemTrayDesired() && SystemTrayIcon::isSystemTrayAreaAvailable()) {
TRAY_ICON_BUBBLE_TIMEOUT, // Use tray icon balloons (which are implemented as native notifications on most systems.
std::move(action.m_action)); trayIcon()->showMessage(msg.m_title.simplified().isEmpty() ? Notification::nameForEvent(notification.event())
: msg.m_title,
msg.m_message,
msg.m_type,
TRAY_ICON_BUBBLE_TIMEOUT,
std::move(action.m_action));
}
return; return;
} }
} }
@ -999,15 +1005,24 @@ void Application::onFeedUpdatesProgress(const Feed* feed, int current, int total
} }
void Application::onFeedUpdatesFinished(const FeedDownloadResults& results) { void Application::onFeedUpdatesFinished(const FeedDownloadResults& results) {
auto fds = results.updatedFeeds(); auto fds = results.updatedFeeds().keys();
bool some_unquiet_feed = boolinq::from(fds).any([](const QPair<Feed*, int>& fd) { bool some_unquiet_feed = boolinq::from(fds).any([](Feed* fd) {
return !fd.first->isQuiet(); return !fd->isQuiet();
}); });
if (some_unquiet_feed) { if (some_unquiet_feed) {
// Now, inform about results via GUI message/notification. GuiMessage msg = {tr("Unread articles fetched"), QString(), QSystemTrayIcon::MessageIcon::NoIcon};
qApp->showGuiMessage(Notification::Event::NewUnreadArticlesFetched,
{tr("Unread articles fetched"), results.overview(10), QSystemTrayIcon::MessageIcon::NoIcon}); if (m_toastNotifications != nullptr) {
// Show custom and richer overview of updated feeds and articles.
msg.m_feedFetchResults = results;
}
else {
// Show simpler overview of updated feeds.
msg.m_message = results.overview(10);
}
qApp->showGuiMessage(Notification::Event::NewUnreadArticlesFetched, msg);
} }
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)

View File

@ -60,6 +60,7 @@ struct GuiMessage {
QString m_title; QString m_title;
QString m_message; QString m_message;
QSystemTrayIcon::MessageIcon m_type; QSystemTrayIcon::MessageIcon m_type;
FeedDownloadResults m_feedFetchResults;
}; };
Q_DECLARE_METATYPE(GuiMessage) Q_DECLARE_METATYPE(GuiMessage)

View File

@ -255,19 +255,6 @@ void FeedReader::removeMessageFilterToFeedAssignment(Feed* feed, MessageFilter*
} }
void FeedReader::updateAllFeeds() { void FeedReader::updateAllFeeds() {
qApp
->showGuiMessage(Notification::Event::GeneralEvent,
GuiMessage(QDateTime::currentDateTime().toString(),
"Quisque ullamcorper ut purus nec tempus. Vivamus eros dolor, sagittis ultrices augue "
"ut, posuere fringilla lorem. Donec posuere, enim sit amet fermentum dignissim, tellus "
"lectus laoreet lectus, vestibulum laoreet felis tortor eget nunc. Curabitur sagittis "
"quam in scelerisque placerat. Vivamus vel porta tortor. Vivamus nec volutpat sem",
QSystemTrayIcon::MessageIcon::Information),
GuiMessageDestination(),
GuiAction("test", []() {
qDebugNN << "aa";
}));
updateFeeds(m_feedsModel->rootItem()->getSubTreeFeeds()); updateFeeds(m_feedsModel->rootItem()->getSubTreeFeeds());
} }

View File

@ -1110,8 +1110,8 @@ ServiceRoot::LabelOperation operator&(ServiceRoot::LabelOperation lhs, ServiceRo
return static_cast<ServiceRoot::LabelOperation>(static_cast<char>(lhs) & static_cast<char>(rhs)); return static_cast<ServiceRoot::LabelOperation>(static_cast<char>(lhs) & static_cast<char>(rhs));
} }
QPair<int, int> ServiceRoot::updateMessages(QList<Message>& messages, Feed* feed, bool force_update, QMutex* db_mutex) { UpdatedArticles ServiceRoot::updateMessages(QList<Message>& messages, Feed* feed, bool force_update, QMutex* db_mutex) {
QPair<int, int> updated_messages = {0, 0}; UpdatedArticles updated_messages;
if (messages.isEmpty()) { if (messages.isEmpty()) {
qDebugNN << "No messages to be updated/added in DB for feed" << QUOTE_W_SPACE_DOT(feed->customId()); qDebugNN << "No messages to be updated/added in DB for feed" << QUOTE_W_SPACE_DOT(feed->customId());
@ -1125,7 +1125,7 @@ QPair<int, int> ServiceRoot::updateMessages(QList<Message>& messages, Feed* feed
updated_messages = DatabaseQueries::updateMessages(database, messages, feed, force_update, db_mutex, &ok); updated_messages = DatabaseQueries::updateMessages(database, messages, feed, force_update, db_mutex, &ok);
if (updated_messages.first > 0 || updated_messages.second > 0) { if (!updated_messages.m_unread.isEmpty() || !updated_messages.m_all.isEmpty()) {
QMutexLocker lck(db_mutex); QMutexLocker lck(db_mutex);
// Something was added or updated in the DB, update numbers. // Something was added or updated in the DB, update numbers.

View File

@ -6,7 +6,6 @@
#include "services/abstract/rootitem.h" #include "services/abstract/rootitem.h"
#include "core/message.h" #include "core/message.h"
#include "core/messagefilter.h"
#include "definitions/typedefs.h" #include "definitions/typedefs.h"
#include <QJsonDocument> #include <QJsonDocument>
@ -206,7 +205,7 @@ class ServiceRoot : public RootItem {
void completelyRemoveAllData(); void completelyRemoveAllData();
// Returns counts of updated messages <unread, all>. // Returns counts of updated messages <unread, all>.
QPair<int, int> updateMessages(QList<Message>& messages, Feed* feed, bool force_update, QMutex* db_mutex); UpdatedArticles updateMessages(QList<Message>& messages, Feed* feed, bool force_update, QMutex* db_mutex);
QIcon feedIconForMessage(const QString& feed_custom_id) const; QIcon feedIconForMessage(const QString& feed_custom_id) const;

View File

@ -418,7 +418,7 @@ QList<Message> FeedlyNetwork::decodeStreamContents(const QByteArray& stream_cont
Message message; Message message;
message.m_feedId = entry_obj[QSL("origin")].toObject()[QSL("streamId")].toString(); message.m_feedId = entry_obj[QSL("origin")].toObject()[QSL("streamId")].toString();
message.m_title = qApp->web()->stripTags(entry_obj[QSL("title")].toString()); message.m_title = entry_obj[QSL("title")].toString();
message.m_author = entry_obj[QSL("author")].toString(); message.m_author = entry_obj[QSL("author")].toString();
message.m_contents = entry_obj[QSL("content")].toObject()[QSL("content")].toString(); message.m_contents = entry_obj[QSL("content")].toObject()[QSL("content")].toString();
message.m_rawContents = QJsonDocument(entry_obj).toJson(QJsonDocument::JsonFormat::Compact); message.m_rawContents = QJsonDocument(entry_obj).toJson(QJsonDocument::JsonFormat::Compact);

View File

@ -957,8 +957,8 @@ QList<Message> GreaderNetwork::decodeStreamContents(ServiceRoot* root,
auto message_obj = obj.toObject(); auto message_obj = obj.toObject();
Message message; Message message;
message.m_title = qApp->web()->unescapeHtml(message_obj[QSL("title")].toString()); message.m_title = message_obj[QSL("title")].toString();
message.m_author = qApp->web()->unescapeHtml(message_obj[QSL("author")].toString()); message.m_author = message_obj[QSL("author")].toString();
message.m_created = QDateTime::fromSecsSinceEpoch(message_obj[QSL("published")].toInt(), Qt::TimeSpec::UTC); message.m_created = QDateTime::fromSecsSinceEpoch(message_obj[QSL("published")].toInt(), Qt::TimeSpec::UTC);
message.m_createdFromFeed = true; message.m_createdFromFeed = true;
message.m_customId = message_obj[QSL("id")].toString(); message.m_customId = message_obj[QSL("id")].toString();

View File

@ -100,9 +100,9 @@ QList<Message> FeedParser::messages() {
Message new_message; Message new_message;
// Fill available data. // Fill available data.
new_message.m_title = qApp->web()->stripTags(qApp->web()->unescapeHtml(xmlMessageTitle(message_item))); new_message.m_title = xmlMessageTitle(message_item);
new_message.m_contents = xmlMessageDescription(message_item); new_message.m_contents = xmlMessageDescription(message_item);
new_message.m_author = qApp->web()->stripTags(qApp->web()->unescapeHtml(xmlMessageAuthor(message_item))); new_message.m_author = xmlMessageAuthor(message_item);
new_message.m_url = xmlMessageUrl(message_item); new_message.m_url = xmlMessageUrl(message_item);
new_message.m_created = xmlMessageDateCreated(message_item); new_message.m_created = xmlMessageDateCreated(message_item);
new_message.m_customId = xmlMessageId(message_item); new_message.m_customId = xmlMessageId(message_item);
@ -128,9 +128,9 @@ QList<Message> FeedParser::messages() {
Message new_message; Message new_message;
// Fill available data. // Fill available data.
new_message.m_title = qApp->web()->stripTags(qApp->web()->unescapeHtml(jsonMessageTitle(message_item))); new_message.m_title = jsonMessageTitle(message_item);
new_message.m_contents = jsonMessageDescription(message_item); new_message.m_contents = jsonMessageDescription(message_item);
new_message.m_author = qApp->web()->stripTags(qApp->web()->unescapeHtml(jsonMessageAuthor(message_item))); new_message.m_author = jsonMessageAuthor(message_item);
new_message.m_url = jsonMessageUrl(message_item); new_message.m_url = jsonMessageUrl(message_item);
new_message.m_created = jsonMessageDateCreated(message_item); new_message.m_created = jsonMessageDateCreated(message_item);
new_message.m_customId = jsonMessageId(message_item); new_message.m_customId = jsonMessageId(message_item);