From 5ae34b56f75f7e55e296e7c2020b313e6196eb01 Mon Sep 17 00:00:00 2001 From: Tobias Fella Date: Fri, 28 Feb 2020 23:25:08 +0100 Subject: [PATCH] Initial Commit --- .clang-format | 88 ++++++++++++++++++++++++++++ .gitignore | 1 + CMakeLists.txt | 33 +++++++++++ README.md | 4 ++ src/CMakeLists.txt | 14 +++++ src/contents/ui/EntryListPage.qml | 80 +++++++++++++++++++++++++ src/contents/ui/FeedListPage.qml | 97 +++++++++++++++++++++++++++++++ src/contents/ui/main.qml | 40 +++++++++++++ src/entry.cpp | 60 +++++++++++++++++++ src/entry.h | 42 +++++++++++++ src/entryListModel.cpp | 92 +++++++++++++++++++++++++++++ src/entryListModel.h | 56 ++++++++++++++++++ src/feed.cpp | 54 +++++++++++++++++ src/feed.h | 45 ++++++++++++++ src/feedListModel.cpp | 87 +++++++++++++++++++++++++++ src/feedListModel.h | 48 +++++++++++++++ src/fetcher.cpp | 69 ++++++++++++++++++++++ src/fetcher.h | 43 ++++++++++++++ src/main.cpp | 65 +++++++++++++++++++++ src/resources.qrc | 7 +++ 20 files changed, 1025 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100755 CMakeLists.txt create mode 100755 README.md create mode 100755 src/CMakeLists.txt create mode 100755 src/contents/ui/EntryListPage.qml create mode 100644 src/contents/ui/FeedListPage.qml create mode 100755 src/contents/ui/main.qml create mode 100644 src/entry.cpp create mode 100644 src/entry.h create mode 100644 src/entryListModel.cpp create mode 100644 src/entryListModel.h create mode 100644 src/feed.cpp create mode 100644 src/feed.h create mode 100644 src/feedListModel.cpp create mode 100644 src/feedListModel.h create mode 100644 src/fetcher.cpp create mode 100644 src/fetcher.h create mode 100644 src/main.cpp create mode 100755 src/resources.qrc diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..7f79f395 --- /dev/null +++ b/.clang-format @@ -0,0 +1,88 @@ +--- +# SPDX-License-Identifier: MIT +# +# Copyright (C) 2019 Christoph Cullmann +# Copyright (C) 2019 Gernot Gebhard +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# Style for C++ +Language: Cpp + +# base is WebKit coding style: https://webkit.org/code-style-guidelines/ +# below are only things set that diverge from this style! +BasedOnStyle: WebKit + +# enforce C++11 (e.g. for std::vector> +Standard: Cpp11 + +# 4 spaces indent +TabWidth: 4 + +# 3 * 80 wide lines +ColumnLimit: 240 + +# sort includes inside line separated groups +SortIncludes: true + +# break before braces on function, namespace and class definitions. +BreakBeforeBraces: Linux + +# CrlInstruction *a; +PointerAlignment: Right + +# horizontally aligns arguments after an open bracket. +AlignAfterOpenBracket: Align + +# align trailing comments +AlignTrailingComments: true + +# don't move all parameters to new line +AllowAllParametersOfDeclarationOnNextLine: false + +# no single line functions +AllowShortFunctionsOnASingleLine: None + +# always break before you encounter multi line strings +AlwaysBreakBeforeMultilineStrings: true + +# don't move arguments to own lines if they are not all on the same +BinPackArguments: false + +# don't move parameters to own lines if they are not all on the same +BinPackParameters: false + +# don't break binary ops +BreakBeforeBinaryOperators: None + +# format C++11 braced lists like function calls +Cpp11BracedListStyle: true + +# remove empty lines +KeepEmptyLinesAtTheStartOfBlocks: false + +# no namespace indentation to keep indent level low +NamespaceIndentation: None + +# we use template< without space. +SpaceAfterTemplateKeyword: false + +# macros for which the opening brace stays attached. +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, forever, Q_FOREVER, QBENCHMARK, QBENCHMARK_ONCE ] diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..567609b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100755 index 00000000..2052df4e --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,33 @@ +project(Alligator) + +cmake_minimum_required(VERSION 2.8.12) +set(KF5_MIN_VERSION "5.18.0") +set(QT_MIN_VERSION "5.5.0") + +if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") + message(FATAL_ERROR "This application requires an out of source build. Please create a separate build directory.") +endif() + +include(FeatureSummary) + +find_package(ECM 5.67.0 REQUIRED NO_MODULE) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) + +include(ECMSetupVersion) +include(ECMGenerateHeaders) +include(KDEInstallDirs) +include(KDECMakeSettings) +include(ECMPoQmTools) +include(KDECompilerSettings NO_POLICY_SCOPE) + +find_package(Qt5 ${QT_MIN_VERSION} REQUIRED NO_MODULE COMPONENTS Core Quick Test Gui Svg QuickControls2 Sql) +find_package(KF5Kirigami2 ${KF5_MIN_VERSION} REQUIRED) +find_package(KF5Syndication ${KF5_MIN_VERSION} REQUIRED) +find_package(KF5KIO ${KF5_MIN_VERSION} REQUIRED) + +set(CMAKE_CXX_STANDARD 11) + +add_subdirectory(src) + +feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/README.md b/README.md new file mode 100755 index 00000000..b9ea7524 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# Alligator + +Alligator is an RSS/Atom feed reader, currently under development. + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100755 index 00000000..e02bd322 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,14 @@ +set(alligator_SRCS + main.cpp + feedListModel.cpp + entryListModel.cpp + fetcher.cpp + feed.cpp + entry.cpp + resources.qrc + ) + +add_executable(alligator ${alligator_SRCS} ${RESOURCES}) +target_link_libraries(alligator Qt5::Core Qt5::Qml Qt5::Quick Qt5::Svg Qt5::Sql KF5::KIOCore KF5::KIOFileWidgets KF5::KIONTLM KF5::Syndication) + +install(TARGETS alligator ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/contents/ui/EntryListPage.qml b/src/contents/ui/EntryListPage.qml new file mode 100755 index 00000000..662480d5 --- /dev/null +++ b/src/contents/ui/EntryListPage.qml @@ -0,0 +1,80 @@ +/** + * Copyright 2020 Tobias Fella + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.7 +import QtQuick.Controls 2.10 as Controls + +import org.kde.kirigami 2.4 as Kirigami + +import org.kde.alligator 1.0 + +Kirigami.ScrollablePage { + id: page + property string name + property string url + title: name + + contextualActions: [ + Kirigami.Action { + text: "Details" + onTriggered: ;//pageStack.push("qrc:/qml/FeedDetailsPage.qml", {"modelData": atomModel}) + } + ] + + + Component.onCompleted: { + entryListModel.fetch(url); + } + + ListView { + model: EntryListModel { + id: entryListModel + feed: url + } + + Connections { + target: entryListModel + } + + delegate: Kirigami.SwipeListItem { + Controls.Label { + id: postTitle + width: parent.width + text: model.display + color: model.read ? Kirigami.Theme.disabledTextColor : Kirigami.Theme.textColor + } + + actions: [ + Kirigami.Action { + icon.name: model.bookmark ? "bookmark-remove" : "bookmark-new" + text: "Bookmark" + onTriggered: { + model.bookmark = !model.bookmark + } + } + ] + + onClicked: { + model.read = true; + //pageStack.push("qrc:/qml/EntryDetailsPage.qml", {"modelData": model}) + } + } + } +} diff --git a/src/contents/ui/FeedListPage.qml b/src/contents/ui/FeedListPage.qml new file mode 100644 index 00000000..a61a94d7 --- /dev/null +++ b/src/contents/ui/FeedListPage.qml @@ -0,0 +1,97 @@ +/** + * Copyright 2020 Tobias Fella + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.14 +import QtQuick.Controls 2.10 as Controls +import QtQuick.Layouts 1.12 + +import org.kde.kirigami 2.4 as Kirigami + +import org.kde.alligator 1.0 + +Component { + Kirigami.ScrollablePage { + title: "Alligator" + + contextualActions: [ + Kirigami.Action { + text: "Add feed" + onTriggered: { + addSheet.open() + } + } + ] + + Kirigami.OverlaySheet { + id: addSheet + contentItem: Kirigami.FormLayout { + Controls.TextField { + id: urlField + Layout.fillWidth: true + //placeholderText: "https://example.org/feed.xml" + text: "https://rss.golem.de/rss.php?feed=RSS2.0" + Kirigami.FormData.label: "Url" + } + Controls.Button { + text: "Add" + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + enabled: urlField.text + onClicked: { + feedListModel.add_feed(urlField.text) + addSheet.close() + } + } + } + } + + ListView { + anchors.fill: parent + model: FeedListModel { + id: feedListModel + } + + delegate: Kirigami.SwipeListItem { + Controls.Label { + text: model.display + } + + onTextChanged: console.log(model.display) + + width: parent.width + height: Kirigami.Units.gridUnit * 2 + actions: [ + Kirigami.Action { + icon.name: "list-remove" + text: "Remove" + onTriggered: feedListModel.remove_feed(index) + }, + Kirigami.Action { + icon.name: "document-edit" + text: "Edit" + onTriggered:; //TODO + } + ] + onClicked: { + pageStack.push("qrc:/EntryListPage.qml", {"name": model.display, "url": model.url}) + } + } + } + } +} diff --git a/src/contents/ui/main.qml b/src/contents/ui/main.qml new file mode 100755 index 00000000..62b4e892 --- /dev/null +++ b/src/contents/ui/main.qml @@ -0,0 +1,40 @@ +/** + * Copyright 2020 Tobias Fella + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.1 +import QtQuick.Controls 2.0 as Controls + +import org.kde.kirigami 2.4 as Kirigami + +Kirigami.ApplicationWindow { + id: root + + title: "Alligator" + + pageStack.initialPage: feedList + + contextDrawer: Kirigami.ContextDrawer { + id: contextDrawer + } + + FeedListPage { + id: feedList + } +} diff --git a/src/entry.cpp b/src/entry.cpp new file mode 100644 index 00000000..eea5d313 --- /dev/null +++ b/src/entry.cpp @@ -0,0 +1,60 @@ +/** + * Copyright 2020 Tobias Fella + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "entry.h" + +Entry::Entry(const QString title, const bool bookmark, const bool read) + : m_title(title) + , m_bookmark(bookmark) + , m_read(read) +{ +} + +Entry::Entry(const Entry &other) + : m_title(other.title()) + , m_bookmark(other.isBookmark()) + , m_read(other.isRead()) +{ +} + +bool Entry::isRead() const +{ + return m_read; +} + +bool Entry::isBookmark() const +{ + return m_bookmark; +} + +QString Entry::title() const +{ + return m_title; +} + +void Entry::setRead(bool read) +{ + m_read = read; +} + +void Entry::setBookmark(bool bookmark) +{ + m_bookmark = bookmark; +} diff --git a/src/entry.h b/src/entry.h new file mode 100644 index 00000000..93eeeefa --- /dev/null +++ b/src/entry.h @@ -0,0 +1,42 @@ +/** + * Copyright 2020 Tobias Fella + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +class Entry +{ +public: + Entry(const Entry &); + Entry(const QString title, const bool bookmark, const bool read); + + QString title() const; + bool isBookmark() const; + bool isRead() const; + + void setRead(bool read); + void setBookmark(bool bookmark); + +private: + QString m_title; + bool m_bookmark; + bool m_read; +}; diff --git a/src/entryListModel.cpp b/src/entryListModel.cpp new file mode 100644 index 00000000..6f9a9b10 --- /dev/null +++ b/src/entryListModel.cpp @@ -0,0 +1,92 @@ +/** + * Copyright 2020 Tobias Fella + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include + +#include "entryListModel.h" +#include "fetcher.h" + +EntryListModel::EntryListModel(QObject *parent) + : QAbstractListModel(parent) +{ +} + +QVariant EntryListModel::data(const QModelIndex &index, int role) const +{ + if (role == Bookmark) + return QVariant(m_entries[index.row()].isBookmark()); + if (role == Read) + return QVariant(m_entries[index.row()].isRead()); + if (role == Qt::DisplayRole) + return m_entries[index.row()].title(); + return QVariant(index.row()); +} +int EntryListModel::rowCount(const QModelIndex &index) const +{ + return m_entries.size(); +} +QHash EntryListModel::roleNames() const +{ + QHash roleNames; + roleNames[Qt::DisplayRole] = "display"; + roleNames[Bookmark] = "bookmark"; + roleNames[Read] = "read"; + return roleNames; +} +bool EntryListModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (role == Bookmark) { + m_entries[index.row()].setBookmark(value.toBool()); + } else if (role == Read) { + m_entries[index.row()].setRead(value.toBool()); + } + emit dataChanged(index, index, QVector({role})); + return true; +} + +void EntryListModel::fetch() +{ + connect(&Fetcher::instance(), &Fetcher::finished, this, [this]() { + beginResetModel(); + QSqlQuery query = QSqlQuery(QSqlDatabase::database()); + query.prepare(QStringLiteral("SELECT id, title, content FROM Entries WHERE feed=:feed;")); + query.bindValue(QStringLiteral(":feed"), m_feed); + query.exec(); + while (query.next()) { + m_entries.append(Entry(query.value(1).toString(), false, false)); + } + endResetModel(); + }); + Fetcher::instance().fetch(m_feed); +} + +QString EntryListModel::feed() const +{ + return m_feed; +} + +void EntryListModel::setFeed(QString feed) +{ + m_feed = feed; +} diff --git a/src/entryListModel.h b/src/entryListModel.h new file mode 100644 index 00000000..44c98948 --- /dev/null +++ b/src/entryListModel.h @@ -0,0 +1,56 @@ +/** + * Copyright 2020 Tobias Fella + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include + +#include + +#include "entry.h" + +class EntryListModel : public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY(QString feed READ feed WRITE setFeed NOTIFY feedChanged) +public: + enum DataRole { + Bookmark = Qt::UserRole + 1, + Read, + }; + explicit EntryListModel(QObject *parent = nullptr); + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + int rowCount(const QModelIndex &index) const override; + QHash roleNames() const override; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + + QString feed() const; + void setFeed(QString feed); + + Q_INVOKABLE void fetch(); + +Q_SIGNALS: + void feedChanged(QString); + +private: + QVector m_entries; + QString m_feed; +}; diff --git a/src/feed.cpp b/src/feed.cpp new file mode 100644 index 00000000..41b5a2d0 --- /dev/null +++ b/src/feed.cpp @@ -0,0 +1,54 @@ +/** + * Copyright 2020 Tobias Fella + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "feed.h" + +Feed::Feed(const QString url) + : m_url(url) + , m_name(url) +{ +} + +Feed::Feed(const QString url, const QString name) + : m_url(url) + , m_name(name) +{ +} + +Feed::Feed(const Feed &other) + : m_url(other.url()) + , m_name(other.name()) +{ +} + +QString Feed::url() const +{ + return m_url; +} + +QString Feed::name() const +{ + return m_name; +} + +void Feed::setName(QString name) +{ + m_name = name; +} diff --git a/src/feed.h b/src/feed.h new file mode 100644 index 00000000..8d8b98e1 --- /dev/null +++ b/src/feed.h @@ -0,0 +1,45 @@ +/** + * Copyright 2020 Tobias Fella + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include + +class Feed : public QObject +{ + Q_OBJECT +public: + Feed(const QString url); + Feed(const Feed &other); + Feed(const QString url, const QString name); + + QString name() const; + QString url() const; + + void setName(QString name); + +Q_SIGNALS: + void nameChanged(const QString &name); + +private: + QString m_url; + QString m_name; +}; diff --git a/src/feedListModel.cpp b/src/feedListModel.cpp new file mode 100644 index 00000000..a87ee833 --- /dev/null +++ b/src/feedListModel.cpp @@ -0,0 +1,87 @@ +/** + * Copyright 2020 Tobias Fella + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include + +#include "feedListModel.h" +#include "fetcher.h" + +FeedListModel::FeedListModel(QObject *parent) + : QAbstractListModel(parent) +{ + QSqlQuery query = QSqlQuery(QSqlDatabase::database()); + query.exec(QStringLiteral("SELECT name, url FROM Feeds")); + beginInsertRows(QModelIndex(), 0, query.size()); + while (query.next()) { + feeds += Feed(query.value(1).toString(), query.value(0).toString()); + } + endInsertRows(); +} + +QHash FeedListModel::roleNames() const +{ + QHash roleNames; + roleNames[Qt::DisplayRole] = "display"; + roleNames[Url] = "url"; + return roleNames; +} + +QVariant FeedListModel::data(const QModelIndex &index, int role) const +{ + if (role == Url) + return QVariant(this->feeds[index.row()].url()); + if (role == Qt::DisplayRole) + return QVariant(this->feeds[index.row()].name()); + return QVariant(); +} +int FeedListModel::rowCount(const QModelIndex &index) const +{ + return this->feeds.size(); +} + +void FeedListModel::add_feed(QString url) +{ + Fetcher::instance().fetch(QUrl(url)); + beginInsertRows(QModelIndex(), feeds.size(), feeds.size()); + feeds.append(Feed(url)); + endInsertRows(); + QSqlQuery query = QSqlQuery(QSqlDatabase::database()); + query.prepare(QStringLiteral("INSERT INTO Feeds VALUES (:url, :name);")); + query.bindValue(QStringLiteral(":url"), url); + query.bindValue(QStringLiteral(":name"), url); + query.exec(); +} + +void FeedListModel::remove_feed(int index) +{ + Feed toRemove = feeds[index]; + QSqlQuery query = QSqlQuery(QSqlDatabase::database()); + query.prepare(QStringLiteral("DELETE FROM Feeds WHERE name=:name AND url=url;")); + query.bindValue(QStringLiteral(":url"), toRemove.url()); + query.bindValue(QStringLiteral(":name"), toRemove.name()); + query.exec(); + beginRemoveRows(QModelIndex(), index, index); + feeds.remove(index); + endRemoveRows(); +} diff --git a/src/feedListModel.h b/src/feedListModel.h new file mode 100644 index 00000000..dc14e5bb --- /dev/null +++ b/src/feedListModel.h @@ -0,0 +1,48 @@ +/** + * Copyright 2020 Tobias Fella + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include + +#include "feed.h" +#include "fetcher.h" + +class FeedListModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum DataRole { + Url = Qt::UserRole + 1, + }; + explicit FeedListModel(QObject *parent = nullptr); + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + int rowCount(const QModelIndex &index) const override; + QHash roleNames() const override; + + Q_INVOKABLE void add_feed(QString url); + Q_INVOKABLE void remove_feed(int index); + +private: + QVector feeds; +}; diff --git a/src/fetcher.cpp b/src/fetcher.cpp new file mode 100644 index 00000000..083e1694 --- /dev/null +++ b/src/fetcher.cpp @@ -0,0 +1,69 @@ +/** + * Copyright 2020 Tobias Fella + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include + +#include "fetcher.h" + +Fetcher::Fetcher() { +} + +void Fetcher::fetch(QUrl url) +{ + QNetworkAccessManager *manager = new QNetworkAccessManager(this); + manager->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy); + manager->setStrictTransportSecurityEnabled(true); + manager->enableStrictTransportSecurityStore(true); + QNetworkRequest request = QNetworkRequest(QUrl(url)); + QNetworkReply *reply = manager->get(request); + connect(reply, &QNetworkReply::finished, this, [this, url, reply]() { + QByteArray data = reply->readAll(); + Syndication::DocumentSource *document = new Syndication::DocumentSource(data, url.toString()); + Syndication::FeedPtr feed = Syndication::parserCollection()->parse(*document, QStringLiteral("Atom")); + + QSqlDatabase db = QSqlDatabase::database(); + QSqlQuery query = QSqlQuery(db); + + for (const auto &entry : feed->items()) { + query = QSqlQuery(db); + query.prepare(QStringLiteral("INSERT INTO Entries VALUES (:feed, :id, :title, :contents);")); + query.bindValue(QStringLiteral(":feed"), url.toString()); + query.bindValue(QStringLiteral(":id"), entry->id()); + query.bindValue(QStringLiteral(":title"), entry->title()); + query.bindValue(QStringLiteral(":contents"), entry->content()); + query.exec(); + for (const auto &author : entry->authors()) { + query = QSqlQuery(db); + query.prepare(QStringLiteral("INSERT INTO Authors VALUES(:id, :name, :uri, :email);")); + query.bindValue(QStringLiteral(":id"), entry->id()); + query.bindValue(QStringLiteral(":name"), author->name()); + query.bindValue(QStringLiteral(":uri"), author->uri()); + query.bindValue(QStringLiteral(":email"), author->email()); + query.exec(); + } + } + emit finished(); + }); +} diff --git a/src/fetcher.h b/src/fetcher.h new file mode 100644 index 00000000..3447e064 --- /dev/null +++ b/src/fetcher.h @@ -0,0 +1,43 @@ +/** + * Copyright 2020 Tobias Fella + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include + +class Fetcher : public QObject +{ + Q_OBJECT +public: + static Fetcher &instance() + { + static Fetcher _instance; + return _instance; + } + void fetch(QUrl); + +private: + Fetcher(); + Fetcher(const Fetcher &); + +Q_SIGNALS: + void finished(); +}; diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 00000000..346d1910 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,65 @@ +/** + * Copyright 2020 Tobias Fella + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "entryListModel.h" +#include "feedListModel.h" + +int main(int argc, char *argv[]) +{ + QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QApplication app(argc, argv); + QCoreApplication::setOrganizationName(QStringLiteral("KDE")); + QCoreApplication::setOrganizationDomain(QStringLiteral("kde.org")); + QCoreApplication::setApplicationName(QStringLiteral("Alligator")); + QCoreApplication::setApplicationVersion(QStringLiteral("0.1")); + + qmlRegisterType("org.kde.alligator", 1, 0, "FeedListModel"); + qmlRegisterType("org.kde.alligator", 1, 0, "EntryListModel"); + + QQmlApplicationEngine engine; + + QSqlDatabase db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE")); + QString databasePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); + QDir(databasePath).mkpath(databasePath); + db.setDatabaseName(databasePath + "/database.db3"); + db.open(); + + QSqlQuery query = QSqlQuery(db); + query.exec(QStringLiteral("CREATE TABLE IF NOT EXISTS Feeds (name TEXT, url TEXT);")); + query.exec(QStringLiteral("CREATE TABLE IF NOT EXISTS Entries (feed TEXT, id TEXT UNIQUE, title TEXT, content TEXT);")); + query.exec(QStringLiteral("CREATE TABLE IF NOT EXISTS Authors (id TEXT, name TEXT, uri TEXT, email TEXT);")); + + engine.load(QUrl(QStringLiteral("qrc:///main.qml"))); + + if (engine.rootObjects().isEmpty()) { + return -1; + } + + return app.exec(); +} diff --git a/src/resources.qrc b/src/resources.qrc new file mode 100755 index 00000000..ca064049 --- /dev/null +++ b/src/resources.qrc @@ -0,0 +1,7 @@ + + + contents/ui/main.qml + contents/ui/EntryListPage.qml + contents/ui/FeedListPage.qml + +