Add implementation for favorites
BUG: 459886
This commit is contained in:
parent
f599380d92
commit
db234722cb
@ -62,6 +62,8 @@ bool Database::migrate()
|
|||||||
TRUE_OR_RETURN(migrateTo5());
|
TRUE_OR_RETURN(migrateTo5());
|
||||||
if (dbversion < 6)
|
if (dbversion < 6)
|
||||||
TRUE_OR_RETURN(migrateTo6());
|
TRUE_OR_RETURN(migrateTo6());
|
||||||
|
if (dbversion < 7)
|
||||||
|
TRUE_OR_RETURN(migrateTo7());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,6 +153,16 @@ bool Database::migrateTo6()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Database::migrateTo7()
|
||||||
|
{
|
||||||
|
qDebug() << "Migrating database to version 7";
|
||||||
|
TRUE_OR_RETURN(transaction());
|
||||||
|
TRUE_OR_RETURN(execute(QStringLiteral("ALTER TABLE Entries ADD COLUMN favorite BOOL DEFAULT 0;")));
|
||||||
|
TRUE_OR_RETURN(execute(QStringLiteral("PRAGMA user_version = 7;")));
|
||||||
|
TRUE_OR_RETURN(commit());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool Database::execute(const QString &query, const QString &connectionName)
|
bool Database::execute(const QString &query, const QString &connectionName)
|
||||||
{
|
{
|
||||||
QSqlQuery q(connectionName);
|
QSqlQuery q(connectionName);
|
||||||
|
@ -42,6 +42,7 @@ private:
|
|||||||
bool migrateTo4();
|
bool migrateTo4();
|
||||||
bool migrateTo5();
|
bool migrateTo5();
|
||||||
bool migrateTo6();
|
bool migrateTo6();
|
||||||
|
bool migrateTo7();
|
||||||
void cleanup();
|
void cleanup();
|
||||||
void setWalMode();
|
void setWalMode();
|
||||||
|
|
||||||
|
@ -185,6 +185,17 @@ int DataManager::newEntryCount(const Feed *feed) const
|
|||||||
return query.value(0).toInt();
|
return query.value(0).toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int DataManager::favoriteEntryCount(const Feed *feed) const
|
||||||
|
{
|
||||||
|
QSqlQuery query;
|
||||||
|
query.prepare(QStringLiteral("SELECT COUNT (id) FROM Entries where feed=:feed AND favorite=1;"));
|
||||||
|
query.bindValue(QStringLiteral(":feed"), feed->url());
|
||||||
|
Database::instance().execute(query);
|
||||||
|
if (!query.next())
|
||||||
|
return -1;
|
||||||
|
return query.value(0).toInt();
|
||||||
|
}
|
||||||
|
|
||||||
void DataManager::removeFeed(Feed *feed)
|
void DataManager::removeFeed(Feed *feed)
|
||||||
{
|
{
|
||||||
QList<Feed *> feeds;
|
QList<Feed *> feeds;
|
||||||
@ -661,6 +672,22 @@ void DataManager::bulkMarkNew(bool state, QStringList list)
|
|||||||
Q_EMIT bulkNewStatusActionFinished();
|
Q_EMIT bulkNewStatusActionFinished();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DataManager::bulkMarkFavoriteByIndex(bool state, QModelIndexList list)
|
||||||
|
{
|
||||||
|
bulkMarkFavorite(state, getIdsFromModelIndexList(list));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataManager::bulkMarkFavorite(bool state, QStringList list)
|
||||||
|
{
|
||||||
|
Database::instance().transaction();
|
||||||
|
for (QString id : list) {
|
||||||
|
getEntry(id)->setFavoriteInternal(state);
|
||||||
|
}
|
||||||
|
Database::instance().commit();
|
||||||
|
|
||||||
|
Q_EMIT bulkFavoriteStatusActionFinished();
|
||||||
|
}
|
||||||
|
|
||||||
void DataManager::bulkQueueStatusByIndex(bool state, QModelIndexList list)
|
void DataManager::bulkQueueStatusByIndex(bool state, QModelIndexList list)
|
||||||
{
|
{
|
||||||
bulkQueueStatus(state, getIdsFromModelIndexList(list));
|
bulkQueueStatus(state, getIdsFromModelIndexList(list));
|
||||||
|
@ -37,6 +37,7 @@ public:
|
|||||||
int entryCount(const int feed_index) const;
|
int entryCount(const int feed_index) const;
|
||||||
int entryCount(const Feed *feed) const;
|
int entryCount(const Feed *feed) const;
|
||||||
int newEntryCount(const Feed *feed) const;
|
int newEntryCount(const Feed *feed) const;
|
||||||
|
int favoriteEntryCount(const Feed *feed) const;
|
||||||
Q_INVOKABLE void addFeed(const QString &url);
|
Q_INVOKABLE void addFeed(const QString &url);
|
||||||
void addFeed(const QString &url, const bool fetch);
|
void addFeed(const QString &url, const bool fetch);
|
||||||
void addFeeds(const QStringList &urls);
|
void addFeeds(const QStringList &urls);
|
||||||
@ -67,12 +68,14 @@ public:
|
|||||||
|
|
||||||
Q_INVOKABLE void bulkMarkRead(bool state, QStringList list);
|
Q_INVOKABLE void bulkMarkRead(bool state, QStringList list);
|
||||||
Q_INVOKABLE void bulkMarkNew(bool state, QStringList list);
|
Q_INVOKABLE void bulkMarkNew(bool state, QStringList list);
|
||||||
|
Q_INVOKABLE void bulkMarkFavorite(bool state, QStringList list);
|
||||||
Q_INVOKABLE void bulkQueueStatus(bool state, QStringList list);
|
Q_INVOKABLE void bulkQueueStatus(bool state, QStringList list);
|
||||||
Q_INVOKABLE void bulkDownloadEnclosures(QStringList list);
|
Q_INVOKABLE void bulkDownloadEnclosures(QStringList list);
|
||||||
Q_INVOKABLE void bulkDeleteEnclosures(QStringList list);
|
Q_INVOKABLE void bulkDeleteEnclosures(QStringList list);
|
||||||
|
|
||||||
Q_INVOKABLE void bulkMarkReadByIndex(bool state, QModelIndexList list);
|
Q_INVOKABLE void bulkMarkReadByIndex(bool state, QModelIndexList list);
|
||||||
Q_INVOKABLE void bulkMarkNewByIndex(bool state, QModelIndexList list);
|
Q_INVOKABLE void bulkMarkNewByIndex(bool state, QModelIndexList list);
|
||||||
|
Q_INVOKABLE void bulkMarkFavoriteByIndex(bool state, QModelIndexList list);
|
||||||
Q_INVOKABLE void bulkQueueStatusByIndex(bool state, QModelIndexList list);
|
Q_INVOKABLE void bulkQueueStatusByIndex(bool state, QModelIndexList list);
|
||||||
Q_INVOKABLE void bulkDownloadEnclosuresByIndex(QModelIndexList list);
|
Q_INVOKABLE void bulkDownloadEnclosuresByIndex(QModelIndexList list);
|
||||||
Q_INVOKABLE void bulkDeleteEnclosuresByIndex(QModelIndexList list);
|
Q_INVOKABLE void bulkDeleteEnclosuresByIndex(QModelIndexList list);
|
||||||
@ -87,9 +90,11 @@ Q_SIGNALS:
|
|||||||
|
|
||||||
void unreadEntryCountChanged(const QString &url);
|
void unreadEntryCountChanged(const QString &url);
|
||||||
void newEntryCountChanged(const QString &url);
|
void newEntryCountChanged(const QString &url);
|
||||||
|
void favoriteEntryCountChanged(const QString &url);
|
||||||
|
|
||||||
void bulkReadStatusActionFinished();
|
void bulkReadStatusActionFinished();
|
||||||
void bulkNewStatusActionFinished();
|
void bulkNewStatusActionFinished();
|
||||||
|
void bulkFavoriteStatusActionFinished();
|
||||||
|
|
||||||
// this will relay the AudioManager::playbackRateChanged signal; this is
|
// this will relay the AudioManager::playbackRateChanged signal; this is
|
||||||
// required to avoid a dependency loop on startup
|
// required to avoid a dependency loop on startup
|
||||||
|
@ -72,6 +72,10 @@ void Entry::updateFromDb(bool emitSignals)
|
|||||||
m_new = entryQuery.value(QStringLiteral("new")).toBool();
|
m_new = entryQuery.value(QStringLiteral("new")).toBool();
|
||||||
Q_EMIT newChanged(m_new);
|
Q_EMIT newChanged(m_new);
|
||||||
}
|
}
|
||||||
|
if (m_favorite != entryQuery.value(QStringLiteral("favorite")).toBool()) {
|
||||||
|
m_favorite = entryQuery.value(QStringLiteral("favorite")).toBool();
|
||||||
|
Q_EMIT favoriteChanged(m_favorite);
|
||||||
|
}
|
||||||
|
|
||||||
setHasEnclosure(entryQuery.value(QStringLiteral("hasEnclosure")).toBool(), emitSignals);
|
setHasEnclosure(entryQuery.value(QStringLiteral("hasEnclosure")).toBool(), emitSignals);
|
||||||
setImage(entryQuery.value(QStringLiteral("image")).toString(), emitSignals);
|
setImage(entryQuery.value(QStringLiteral("image")).toString(), emitSignals);
|
||||||
@ -172,6 +176,11 @@ bool Entry::getNew() const
|
|||||||
return m_new;
|
return m_new;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Entry::favorite() const
|
||||||
|
{
|
||||||
|
return m_favorite;
|
||||||
|
}
|
||||||
|
|
||||||
QString Entry::baseUrl() const
|
QString Entry::baseUrl() const
|
||||||
{
|
{
|
||||||
return QUrl(m_link).adjusted(QUrl::RemovePath).toString();
|
return QUrl(m_link).adjusted(QUrl::RemovePath).toString();
|
||||||
@ -340,6 +349,34 @@ void Entry::setNewInternal(bool state)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Entry::setFavorite(bool favorite)
|
||||||
|
{
|
||||||
|
if (favorite != m_favorite) {
|
||||||
|
// Making a detour through DataManager to make bulk operations more
|
||||||
|
// performant. DataManager will call setFavoriteInternal on every item to
|
||||||
|
// be marked new/not new. So implement features there.
|
||||||
|
DataManager::instance().bulkMarkFavorite(favorite, QStringList(m_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Entry::setFavoriteInternal(bool favorite)
|
||||||
|
{
|
||||||
|
if (favorite != m_favorite) {
|
||||||
|
// Make sure that operations done here can be wrapped inside an sqlite
|
||||||
|
// transaction. I.e. no calls that trigger a SELECT operation.
|
||||||
|
m_favorite = favorite;
|
||||||
|
Q_EMIT favoriteChanged(m_favorite);
|
||||||
|
|
||||||
|
QSqlQuery query;
|
||||||
|
query.prepare(QStringLiteral("UPDATE Entries SET favorite=:favorite WHERE id=:id;"));
|
||||||
|
query.bindValue(QStringLiteral(":id"), m_id);
|
||||||
|
query.bindValue(QStringLiteral(":favorite"), m_favorite);
|
||||||
|
Database::instance().execute(query);
|
||||||
|
|
||||||
|
Q_EMIT DataManager::instance().favoriteEntryCountChanged(m_feed->url());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QString Entry::adjustedContent(int width, int fontSize)
|
QString Entry::adjustedContent(int width, int fontSize)
|
||||||
{
|
{
|
||||||
QString ret(m_content);
|
QString ret(m_content);
|
||||||
|
@ -32,6 +32,7 @@ class Entry : public QObject
|
|||||||
Q_PROPERTY(QString baseUrl READ baseUrl NOTIFY baseUrlChanged)
|
Q_PROPERTY(QString baseUrl READ baseUrl NOTIFY baseUrlChanged)
|
||||||
Q_PROPERTY(bool read READ read WRITE setRead NOTIFY readChanged)
|
Q_PROPERTY(bool read READ read WRITE setRead NOTIFY readChanged)
|
||||||
Q_PROPERTY(bool new READ getNew WRITE setNew NOTIFY newChanged)
|
Q_PROPERTY(bool new READ getNew WRITE setNew NOTIFY newChanged)
|
||||||
|
Q_PROPERTY(bool favorite READ favorite WRITE setFavorite NOTIFY favoriteChanged)
|
||||||
Q_PROPERTY(Enclosure *enclosure READ enclosure CONSTANT)
|
Q_PROPERTY(Enclosure *enclosure READ enclosure CONSTANT)
|
||||||
Q_PROPERTY(bool hasEnclosure READ hasEnclosure NOTIFY hasEnclosureChanged)
|
Q_PROPERTY(bool hasEnclosure READ hasEnclosure NOTIFY hasEnclosureChanged)
|
||||||
Q_PROPERTY(QString image READ image NOTIFY imageChanged)
|
Q_PROPERTY(QString image READ image NOTIFY imageChanged)
|
||||||
@ -51,6 +52,7 @@ public:
|
|||||||
QString link() const;
|
QString link() const;
|
||||||
bool read() const;
|
bool read() const;
|
||||||
bool getNew() const;
|
bool getNew() const;
|
||||||
|
bool favorite() const;
|
||||||
Enclosure *enclosure() const;
|
Enclosure *enclosure() const;
|
||||||
bool hasEnclosure() const;
|
bool hasEnclosure() const;
|
||||||
QString image() const;
|
QString image() const;
|
||||||
@ -62,12 +64,14 @@ public:
|
|||||||
|
|
||||||
void setRead(bool read);
|
void setRead(bool read);
|
||||||
void setNew(bool state);
|
void setNew(bool state);
|
||||||
|
void setFavorite(bool favorite);
|
||||||
void setQueueStatus(bool status);
|
void setQueueStatus(bool status);
|
||||||
|
|
||||||
Q_INVOKABLE QString adjustedContent(int width, int fontSize);
|
Q_INVOKABLE QString adjustedContent(int width, int fontSize);
|
||||||
|
|
||||||
void setNewInternal(bool state);
|
void setNewInternal(bool state);
|
||||||
void setReadInternal(bool read);
|
void setReadInternal(bool read);
|
||||||
|
void setFavoriteInternal(bool favorite);
|
||||||
void setQueueStatusInternal(bool state);
|
void setQueueStatusInternal(bool state);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
@ -80,6 +84,7 @@ Q_SIGNALS:
|
|||||||
void baseUrlChanged(const QString &baseUrl);
|
void baseUrlChanged(const QString &baseUrl);
|
||||||
void readChanged(bool read);
|
void readChanged(bool read);
|
||||||
void newChanged(bool state);
|
void newChanged(bool state);
|
||||||
|
void favoriteChanged(bool favorite);
|
||||||
void hasEnclosureChanged(bool hasEnclosure);
|
void hasEnclosureChanged(bool hasEnclosure);
|
||||||
void imageChanged(const QString &url);
|
void imageChanged(const QString &url);
|
||||||
void cachedImageChanged(const QString &imagePath);
|
void cachedImageChanged(const QString &imagePath);
|
||||||
@ -106,6 +111,7 @@ private:
|
|||||||
QString m_link;
|
QString m_link;
|
||||||
bool m_read;
|
bool m_read;
|
||||||
bool m_new;
|
bool m_new;
|
||||||
|
bool m_favorite;
|
||||||
Enclosure *m_enclosure = nullptr;
|
Enclosure *m_enclosure = nullptr;
|
||||||
QString m_image;
|
QString m_image;
|
||||||
bool m_hasenclosure = false;
|
bool m_hasenclosure = false;
|
||||||
|
@ -215,6 +215,11 @@ int Feed::newEntryCount() const
|
|||||||
return DataManager::instance().newEntryCount(this);
|
return DataManager::instance().newEntryCount(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Feed::favoriteEntryCount() const
|
||||||
|
{
|
||||||
|
return DataManager::instance().favoriteEntryCount(this);
|
||||||
|
}
|
||||||
|
|
||||||
bool Feed::refreshing() const
|
bool Feed::refreshing() const
|
||||||
{
|
{
|
||||||
return m_refreshing;
|
return m_refreshing;
|
||||||
|
@ -35,6 +35,7 @@ class Feed : public QObject
|
|||||||
Q_PROPERTY(int entryCount READ entryCount NOTIFY entryCountChanged)
|
Q_PROPERTY(int entryCount READ entryCount NOTIFY entryCountChanged)
|
||||||
Q_PROPERTY(int unreadEntryCount READ unreadEntryCount WRITE setUnreadEntryCount NOTIFY unreadEntryCountChanged)
|
Q_PROPERTY(int unreadEntryCount READ unreadEntryCount WRITE setUnreadEntryCount NOTIFY unreadEntryCountChanged)
|
||||||
Q_PROPERTY(int newEntryCount READ newEntryCount NOTIFY newEntryCountChanged)
|
Q_PROPERTY(int newEntryCount READ newEntryCount NOTIFY newEntryCountChanged)
|
||||||
|
Q_PROPERTY(int favoriteEntryCount READ favoriteEntryCount NOTIFY favoriteEntryCountChanged)
|
||||||
Q_PROPERTY(int errorId READ errorId WRITE setErrorId NOTIFY errorIdChanged)
|
Q_PROPERTY(int errorId READ errorId WRITE setErrorId NOTIFY errorIdChanged)
|
||||||
Q_PROPERTY(QString errorString READ errorString WRITE setErrorString NOTIFY errorStringChanged)
|
Q_PROPERTY(QString errorString READ errorString WRITE setErrorString NOTIFY errorStringChanged)
|
||||||
Q_PROPERTY(EntriesProxyModel *entries MEMBER m_entries CONSTANT)
|
Q_PROPERTY(EntriesProxyModel *entries MEMBER m_entries CONSTANT)
|
||||||
@ -61,6 +62,7 @@ public:
|
|||||||
int entryCount() const;
|
int entryCount() const;
|
||||||
int unreadEntryCount() const;
|
int unreadEntryCount() const;
|
||||||
int newEntryCount() const;
|
int newEntryCount() const;
|
||||||
|
int favoriteEntryCount() const;
|
||||||
bool read() const;
|
bool read() const;
|
||||||
int errorId() const;
|
int errorId() const;
|
||||||
QString errorString() const;
|
QString errorString() const;
|
||||||
@ -97,6 +99,7 @@ Q_SIGNALS:
|
|||||||
void entryCountChanged();
|
void entryCountChanged();
|
||||||
void unreadEntryCountChanged();
|
void unreadEntryCountChanged();
|
||||||
void newEntryCountChanged();
|
void newEntryCountChanged();
|
||||||
|
void favoriteEntryCountChanged();
|
||||||
void errorIdChanged(int &errorId);
|
void errorIdChanged(int &errorId);
|
||||||
void errorStringChanged(const QString &errorString);
|
void errorStringChanged(const QString &errorString);
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ QHash<int, QByteArray> AbstractEpisodeModel::roleNames() const
|
|||||||
{IdRole, "id"},
|
{IdRole, "id"},
|
||||||
{ReadRole, "read"},
|
{ReadRole, "read"},
|
||||||
{NewRole, "new"},
|
{NewRole, "new"},
|
||||||
|
{FavoriteRole, "favorite"},
|
||||||
{ContentRole, "content"},
|
{ContentRole, "content"},
|
||||||
{FeedNameRole, "feedname"},
|
{FeedNameRole, "feedname"},
|
||||||
};
|
};
|
||||||
|
@ -22,6 +22,7 @@ public:
|
|||||||
IdRole,
|
IdRole,
|
||||||
ReadRole,
|
ReadRole,
|
||||||
NewRole,
|
NewRole,
|
||||||
|
FavoriteRole,
|
||||||
ContentRole,
|
ContentRole,
|
||||||
FeedNameRole,
|
FeedNameRole,
|
||||||
};
|
};
|
||||||
|
@ -40,6 +40,12 @@ bool AbstractEpisodeProxyModel::filterAcceptsRow(int sourceRow, const QModelInde
|
|||||||
case NotNewFilter:
|
case NotNewFilter:
|
||||||
accepted = !sourceModel()->data(index, AbstractEpisodeModel::Roles::NewRole).value<bool>();
|
accepted = !sourceModel()->data(index, AbstractEpisodeModel::Roles::NewRole).value<bool>();
|
||||||
break;
|
break;
|
||||||
|
case FavoriteFilter:
|
||||||
|
accepted = sourceModel()->data(index, AbstractEpisodeModel::Roles::FavoriteRole).value<bool>();
|
||||||
|
break;
|
||||||
|
case NotFavoriteFilter:
|
||||||
|
accepted = !sourceModel()->data(index, AbstractEpisodeModel::Roles::FavoriteRole).value<bool>();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
accepted = true;
|
accepted = true;
|
||||||
break;
|
break;
|
||||||
@ -94,6 +100,7 @@ void AbstractEpisodeProxyModel::setFilterType(FilterType type)
|
|||||||
if (type != m_currentFilter) {
|
if (type != m_currentFilter) {
|
||||||
disconnect(&DataManager::instance(), &DataManager::bulkReadStatusActionFinished, this, nullptr);
|
disconnect(&DataManager::instance(), &DataManager::bulkReadStatusActionFinished, this, nullptr);
|
||||||
disconnect(&DataManager::instance(), &DataManager::bulkNewStatusActionFinished, this, nullptr);
|
disconnect(&DataManager::instance(), &DataManager::bulkNewStatusActionFinished, this, nullptr);
|
||||||
|
disconnect(&DataManager::instance(), &DataManager::bulkFavoriteStatusActionFinished, this, nullptr);
|
||||||
|
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
m_currentFilter = type;
|
m_currentFilter = type;
|
||||||
@ -113,6 +120,12 @@ void AbstractEpisodeProxyModel::setFilterType(FilterType type)
|
|||||||
dynamic_cast<AbstractEpisodeModel *>(sourceModel())->updateInternalState();
|
dynamic_cast<AbstractEpisodeModel *>(sourceModel())->updateInternalState();
|
||||||
endResetModel();
|
endResetModel();
|
||||||
});
|
});
|
||||||
|
} else if (type == FavoriteFilter || type == NotFavoriteFilter) {
|
||||||
|
connect(&DataManager::instance(), &DataManager::bulkFavoriteStatusActionFinished, this, [this]() {
|
||||||
|
beginResetModel();
|
||||||
|
dynamic_cast<AbstractEpisodeModel *>(sourceModel())->updateInternalState();
|
||||||
|
endResetModel();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_EMIT filterTypeChanged();
|
Q_EMIT filterTypeChanged();
|
||||||
@ -152,6 +165,10 @@ QString AbstractEpisodeProxyModel::getFilterName(FilterType type) const
|
|||||||
return i18nc("@label:chooser Choice of filter for episode list", "Episodes marked as \"New\"");
|
return i18nc("@label:chooser Choice of filter for episode list", "Episodes marked as \"New\"");
|
||||||
case FilterType::NotNewFilter:
|
case FilterType::NotNewFilter:
|
||||||
return i18nc("@label:chooser Choice of filter for episode list", "Episodes not marked as \"New\"");
|
return i18nc("@label:chooser Choice of filter for episode list", "Episodes not marked as \"New\"");
|
||||||
|
case FilterType::FavoriteFilter:
|
||||||
|
return i18nc("@label:chooser Choice of filter for episode list", "Episodes marked as Favorite");
|
||||||
|
case FilterType::NotFavoriteFilter:
|
||||||
|
return i18nc("@label:chooser Choice of filter for episode list", "Episodes not marked as Favorite");
|
||||||
default:
|
default:
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@ public:
|
|||||||
NotReadFilter,
|
NotReadFilter,
|
||||||
NewFilter,
|
NewFilter,
|
||||||
NotNewFilter,
|
NotNewFilter,
|
||||||
|
FavoriteFilter,
|
||||||
|
NotFavoriteFilter,
|
||||||
};
|
};
|
||||||
Q_ENUM(FilterType)
|
Q_ENUM(FilterType)
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@ QHash<int, QByteArray> DownloadModel::roleNames() const
|
|||||||
{EpisodeModel::Roles::IdRole, "id"},
|
{EpisodeModel::Roles::IdRole, "id"},
|
||||||
{EpisodeModel::Roles::ReadRole, "read"},
|
{EpisodeModel::Roles::ReadRole, "read"},
|
||||||
{EpisodeModel::Roles::NewRole, "new"},
|
{EpisodeModel::Roles::NewRole, "new"},
|
||||||
|
{EpisodeModel::Roles::FavoriteRole, "favorite"},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,6 +42,8 @@ QVariant EntriesModel::data(const QModelIndex &index, int role) const
|
|||||||
return QVariant::fromValue(entry->read());
|
return QVariant::fromValue(entry->read());
|
||||||
case AbstractEpisodeModel::Roles::NewRole:
|
case AbstractEpisodeModel::Roles::NewRole:
|
||||||
return QVariant::fromValue(entry->getNew());
|
return QVariant::fromValue(entry->getNew());
|
||||||
|
case AbstractEpisodeModel::Roles::FavoriteRole:
|
||||||
|
return QVariant::fromValue(entry->favorite());
|
||||||
case AbstractEpisodeModel::Roles::ContentRole:
|
case AbstractEpisodeModel::Roles::ContentRole:
|
||||||
return QVariant::fromValue(entry->content());
|
return QVariant::fromValue(entry->content());
|
||||||
case AbstractEpisodeModel::Roles::FeedNameRole:
|
case AbstractEpisodeModel::Roles::FeedNameRole:
|
||||||
|
@ -41,6 +41,8 @@ QVariant EpisodeModel::data(const QModelIndex &index, int role) const
|
|||||||
return QVariant::fromValue(m_read[index.row()]);
|
return QVariant::fromValue(m_read[index.row()]);
|
||||||
case AbstractEpisodeModel::Roles::NewRole:
|
case AbstractEpisodeModel::Roles::NewRole:
|
||||||
return QVariant::fromValue(m_new[index.row()]);
|
return QVariant::fromValue(m_new[index.row()]);
|
||||||
|
case AbstractEpisodeModel::Roles::FavoriteRole:
|
||||||
|
return QVariant::fromValue(m_favorite[index.row()]);
|
||||||
case AbstractEpisodeModel::Roles::ContentRole:
|
case AbstractEpisodeModel::Roles::ContentRole:
|
||||||
return QVariant::fromValue(m_contents[index.row()]);
|
return QVariant::fromValue(m_contents[index.row()]);
|
||||||
case AbstractEpisodeModel::Roles::FeedNameRole:
|
case AbstractEpisodeModel::Roles::FeedNameRole:
|
||||||
@ -61,17 +63,19 @@ void EpisodeModel::updateInternalState()
|
|||||||
m_entryIds.clear();
|
m_entryIds.clear();
|
||||||
m_read.clear();
|
m_read.clear();
|
||||||
m_new.clear();
|
m_new.clear();
|
||||||
|
m_favorite.clear();
|
||||||
m_titles.clear();
|
m_titles.clear();
|
||||||
m_contents.clear();
|
m_contents.clear();
|
||||||
m_feedNames.clear();
|
m_feedNames.clear();
|
||||||
|
|
||||||
QSqlQuery query;
|
QSqlQuery query;
|
||||||
query.prepare(QStringLiteral("SELECT id, read, new, title, content, feed FROM Entries ORDER BY updated DESC;"));
|
query.prepare(QStringLiteral("SELECT id, read, new, favorite, title, content, feed FROM Entries ORDER BY updated DESC;"));
|
||||||
Database::instance().execute(query);
|
Database::instance().execute(query);
|
||||||
while (query.next()) {
|
while (query.next()) {
|
||||||
m_entryIds += query.value(QStringLiteral("id")).toString();
|
m_entryIds += query.value(QStringLiteral("id")).toString();
|
||||||
m_read += query.value(QStringLiteral("read")).toBool();
|
m_read += query.value(QStringLiteral("read")).toBool();
|
||||||
m_new += query.value(QStringLiteral("new")).toBool();
|
m_new += query.value(QStringLiteral("new")).toBool();
|
||||||
|
m_favorite += query.value(QStringLiteral("favorite")).toBool();
|
||||||
m_titles += query.value(QStringLiteral("title")).toString();
|
m_titles += query.value(QStringLiteral("title")).toString();
|
||||||
m_contents += query.value(QStringLiteral("content")).toString();
|
m_contents += query.value(QStringLiteral("content")).toString();
|
||||||
m_feedNames += DataManager::instance().getFeed(query.value(QStringLiteral("feed")).toString())->name();
|
m_feedNames += DataManager::instance().getFeed(query.value(QStringLiteral("feed")).toString())->name();
|
||||||
|
@ -35,6 +35,7 @@ private:
|
|||||||
QStringList m_entryIds;
|
QStringList m_entryIds;
|
||||||
QVector<bool> m_read;
|
QVector<bool> m_read;
|
||||||
QVector<bool> m_new;
|
QVector<bool> m_new;
|
||||||
|
QVector<bool> m_favorite;
|
||||||
QStringList m_titles;
|
QStringList m_titles;
|
||||||
QStringList m_contents;
|
QStringList m_contents;
|
||||||
QStringList m_feedNames;
|
QStringList m_feedNames;
|
||||||
|
@ -206,6 +206,14 @@ Kirigami.ScrollablePage {
|
|||||||
entry.new = !entry.new
|
entry.new = !entry.new
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Kirigami.Action {
|
||||||
|
text: entry.favorite ? i18nc("@action:intoolbar Button to remove the \"favorite\" property of a podcast episode", "Remove from Favorites") : i18nc("@action:intoolbar Button to add a podcast episode as favorite", "Add to Favorites")
|
||||||
|
icon.name: !entry.favorite ? "starred-symbolic" : "non-starred-symbolic"
|
||||||
|
displayHint: Kirigami.DisplayHint.AlwaysHide
|
||||||
|
onTriggered: {
|
||||||
|
entry.favorite = !entry.favorite
|
||||||
|
}
|
||||||
|
},
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: i18nc("@action:intoolbar Button to open the podcast URL in browser", "Open Podcast")
|
text: i18nc("@action:intoolbar Button to open the podcast URL in browser", "Open Podcast")
|
||||||
displayHint: Kirigami.DisplayHint.AlwaysHide
|
displayHint: Kirigami.DisplayHint.AlwaysHide
|
||||||
|
@ -180,6 +180,13 @@ Kirigami.SwipeListItem {
|
|||||||
visible: entry ? entry.new : false
|
visible: entry ? entry.new : false
|
||||||
opacity: 0.7
|
opacity: 0.7
|
||||||
}
|
}
|
||||||
|
Kirigami.Icon {
|
||||||
|
Layout.maximumHeight: 0.8 * supertitle.implicitHeight
|
||||||
|
Layout.maximumWidth: 0.8 * supertitle.implicitHeight
|
||||||
|
source: "starred-symbolic"
|
||||||
|
visible: entry ? (entry.favorite) : false
|
||||||
|
opacity: 0.7
|
||||||
|
}
|
||||||
Kirigami.Icon {
|
Kirigami.Icon {
|
||||||
Layout.maximumHeight: 0.8 * supertitle.implicitHeight
|
Layout.maximumHeight: 0.8 * supertitle.implicitHeight
|
||||||
Layout.maximumWidth: 0.8 * supertitle.implicitHeight
|
Layout.maximumWidth: 0.8 * supertitle.implicitHeight
|
||||||
@ -189,7 +196,7 @@ Kirigami.SwipeListItem {
|
|||||||
}
|
}
|
||||||
Controls.Label {
|
Controls.Label {
|
||||||
id: supertitle
|
id: supertitle
|
||||||
text: entry ? ((!isQueue && entry.queueStatus ? "· " : "") + entry.updated.toLocaleDateString(Qt.locale(), Locale.NarrowFormat) + (entry.enclosure ? ( entry.enclosure.size !== 0 ? " · " + entry.enclosure.formattedSize : "") : "" )) : ""
|
text: entry ? (((!isQueue && entry.queueStatus) || entry.favorite ? "· " : "") + entry.updated.toLocaleDateString(Qt.locale(), Locale.NarrowFormat) + (entry.enclosure ? ( entry.enclosure.size !== 0 ? " · " + entry.enclosure.formattedSize : "") : "" )) : ""
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
font: Kirigami.Theme.smallFont
|
font: Kirigami.Theme.smallFont
|
||||||
|
@ -193,6 +193,24 @@ ListView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property var markFavoriteAction: Kirigami.Action {
|
||||||
|
text: i18nc("@action:intoolbar Button to add a podcast episode as favorite", "Add to Favorites")
|
||||||
|
icon.name: "starred-symbolic"
|
||||||
|
visible: listView.selectionModel.hasSelection && (singleSelectedEntry ? !singleSelectedEntry.favorite : true)
|
||||||
|
onTriggered: {
|
||||||
|
DataManager.bulkMarkFavoriteByIndex(true, selectionForContextMenu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property var markNotFavoriteAction: Kirigami.Action {
|
||||||
|
text: i18nc("@action:intoolbar Button to remove the \"favorite\" property of a podcast episode", "Remove from Favorites")
|
||||||
|
icon.name: "non-starred-symbolic"
|
||||||
|
visible: listView.selectionModel.hasSelection && (singleSelectedEntry ? singleSelectedEntry.favorite : true)
|
||||||
|
onTriggered: {
|
||||||
|
DataManager.bulkMarkFavoriteByIndex(false, selectionForContextMenu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
property var downloadEnclosureAction: Kirigami.Action {
|
property var downloadEnclosureAction: Kirigami.Action {
|
||||||
text: i18n("Download")
|
text: i18n("Download")
|
||||||
icon.name: "download"
|
icon.name: "download"
|
||||||
@ -231,6 +249,8 @@ ListView {
|
|||||||
markNotPlayedAction,
|
markNotPlayedAction,
|
||||||
markNewAction,
|
markNewAction,
|
||||||
markNotNewAction,
|
markNotNewAction,
|
||||||
|
markFavoriteAction,
|
||||||
|
markNotFavoriteAction,
|
||||||
downloadEnclosureAction,
|
downloadEnclosureAction,
|
||||||
deleteEnclosureAction,
|
deleteEnclosureAction,
|
||||||
streamAction,
|
streamAction,
|
||||||
@ -270,6 +290,16 @@ ListView {
|
|||||||
visible: singleSelectedEntry ? singleSelectedEntry.new : true
|
visible: singleSelectedEntry ? singleSelectedEntry.new : true
|
||||||
height: visible ? implicitHeight : 0 // workaround for qqc2-breeze-style
|
height: visible ? implicitHeight : 0 // workaround for qqc2-breeze-style
|
||||||
}
|
}
|
||||||
|
Controls.MenuItem {
|
||||||
|
action: listView.markFavoriteAction
|
||||||
|
visible: singleSelectedEntry ? !singleSelectedEntry.favorite : true
|
||||||
|
height: visible ? implicitHeight : 0 // workaround for qqc2-breeze-style
|
||||||
|
}
|
||||||
|
Controls.MenuItem {
|
||||||
|
action: listView.markNotFavoriteAction
|
||||||
|
visible: singleSelectedEntry ? singleSelectedEntry.favorite : true
|
||||||
|
height: visible ? implicitHeight : 0 // workaround for qqc2-breeze-style
|
||||||
|
}
|
||||||
Controls.MenuItem {
|
Controls.MenuItem {
|
||||||
action: listView.downloadEnclosureAction
|
action: listView.downloadEnclosureAction
|
||||||
visible: singleSelectedEntry ? (singleSelectedEntry.hasEnclosure ? singleSelectedEntry.enclosure.status !== Enclosure.Downloaded : false) : true
|
visible: singleSelectedEntry ? (singleSelectedEntry.hasEnclosure ? singleSelectedEntry.enclosure.status !== Enclosure.Downloaded : false) : true
|
||||||
|
@ -113,7 +113,9 @@ Controls.Control {
|
|||||||
AbstractEpisodeProxyModel.ReadFilter,
|
AbstractEpisodeProxyModel.ReadFilter,
|
||||||
AbstractEpisodeProxyModel.NotReadFilter,
|
AbstractEpisodeProxyModel.NotReadFilter,
|
||||||
AbstractEpisodeProxyModel.NewFilter,
|
AbstractEpisodeProxyModel.NewFilter,
|
||||||
AbstractEpisodeProxyModel.NotNewFilter]
|
AbstractEpisodeProxyModel.NotNewFilter,
|
||||||
|
AbstractEpisodeProxyModel.FavoriteFilter,
|
||||||
|
AbstractEpisodeProxyModel.NotFavoriteFilter]
|
||||||
for (var i in filterList) {
|
for (var i in filterList) {
|
||||||
filterModel.append({"name": proxyModel.getFilterName(filterList[i]),
|
filterModel.append({"name": proxyModel.getFilterName(filterList[i]),
|
||||||
"filterType": filterList[i]});
|
"filterType": filterList[i]});
|
||||||
|
@ -490,7 +490,7 @@ void UpdateFeedJob::writeToDatabase()
|
|||||||
|
|
||||||
// new entries
|
// new entries
|
||||||
writeQuery.prepare(
|
writeQuery.prepare(
|
||||||
QStringLiteral("INSERT INTO Entries VALUES (:feed, :id, :title, :content, :created, :updated, :link, :read, :new, :hasEnclosure, :image);"));
|
QStringLiteral("INSERT INTO Entries VALUES (:feed, :id, :title, :content, :created, :updated, :link, :read, :new, :hasEnclosure, :image, :favorite);"));
|
||||||
for (const EntryDetails &entryDetails : m_newEntries) {
|
for (const EntryDetails &entryDetails : m_newEntries) {
|
||||||
writeQuery.bindValue(QStringLiteral(":feed"), entryDetails.feed);
|
writeQuery.bindValue(QStringLiteral(":feed"), entryDetails.feed);
|
||||||
writeQuery.bindValue(QStringLiteral(":id"), entryDetails.id);
|
writeQuery.bindValue(QStringLiteral(":id"), entryDetails.id);
|
||||||
@ -503,6 +503,7 @@ void UpdateFeedJob::writeToDatabase()
|
|||||||
writeQuery.bindValue(QStringLiteral(":read"), entryDetails.read);
|
writeQuery.bindValue(QStringLiteral(":read"), entryDetails.read);
|
||||||
writeQuery.bindValue(QStringLiteral(":new"), entryDetails.isNew);
|
writeQuery.bindValue(QStringLiteral(":new"), entryDetails.isNew);
|
||||||
writeQuery.bindValue(QStringLiteral(":image"), entryDetails.image);
|
writeQuery.bindValue(QStringLiteral(":image"), entryDetails.image);
|
||||||
|
writeQuery.bindValue(QStringLiteral(":favorite"), false);
|
||||||
Database::execute(writeQuery);
|
Database::execute(writeQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user