work on toasts, unify article sanitization
This commit is contained in:
parent
69dc7e0a32
commit
46fe50df91
@ -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>
|
||||||
|
@ -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
|
||||||
|
23
src/librssguard/core/articlelistnotificationmodel.cpp
Normal file
23
src/librssguard/core/articlelistnotificationmodel.cpp
Normal 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 {}
|
33
src/librssguard/core/articlelistnotificationmodel.h
Normal file
33
src/librssguard/core/articlelistnotificationmodel.h
Normal 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
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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,
|
||||||
|
@ -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"
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
130
src/librssguard/gui/notifications/articlelistnotification.ui
Normal file
130
src/librssguard/gui/notifications/articlelistnotification.ui
Normal 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>
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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>
|
||||||
|
@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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())));
|
||||||
|
}
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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>
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user