Clean up and initialized everything for v3 written in Rust and GTK

This commit just removes all the Qt and QML code and sets up
everything as preperation for the rewrite in Rust and GTK.

There is no new code yet, just a hello world, but the structure
is there.
This commit is contained in:
Jeena 2020-08-12 22:10:56 +02:00
parent 4b804873a6
commit 88a97bb555
24 changed files with 33 additions and 1639 deletions

6
.gitignore vendored
View file

@ -1,3 +1,3 @@
.DS_Store #Added by cargo
FeedTheMonkey.pro.user*
build /target

6
Cargo.lock generated Normal file
View file

@ -0,0 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "feedthemonkey"
version = "3.0.0"

14
Cargo.toml Normal file
View file

@ -0,0 +1,14 @@
[package]
name = "feedthemonkey"
version = "3.0.0"
authors = ["Jeena <hello@jeena.net>"]
edition = "2018"
readme = "README.md"
repository = "https://github.com/jeena/feedthemonkey"
descrition = "Desktop client for the TinyTinyRSS feed reader"
licese = "GPL-3.0-or-later"
license-file = "COPYING"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View file

@ -1,66 +0,0 @@
requires(contains(QT_CONFIG, accessibility))
qtHaveModule(widgets) {
QT += widgets # QApplication is required to get native styling with QtQuickControls
}
TARGET = feedthemonkey
TEMPLATE = app
QT += qml quick webenginewidgets webengine
CONFIG += c++11
CONFIG += qtquickcompiler
SOURCES += \
src/main.cpp \
src/post.cpp \
src/tinytinyrss.cpp \
src/tinytinyrsslogin.cpp
RESOURCES += \
html/html.qrc \
qml/qml.qrc \
mac {
RC_FILE = misc/Icon.icns
TARGET = FeedTheMonkey
}
unix {
isEmpty(PREFIX) {
PREFIX = /usr/local
}
target.path = $$PREFIX/bin
shortcutfiles.files = misc/feedthemonkey.desktop
shortcutfiles.path = $$PREFIX/share/applications/
data.files += misc/feedthemonkey.xpm
data.path = $$PREFIX/share/pixmaps/
INSTALLS += shortcutfiles
INSTALLS += data
}
INSTALLS += target
# Needed for bringing browser from background to foreground using QDesktopServices: http://bugreports.qt-project.org/browse/QTBUG-8336
TARGET.CAPABILITY += SwEvent
# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =
OTHER_FILES +=
HEADERS += \
src/post.h \
src/tinytinyrss.h \
src/tinytinyrsslogin.h
DISTFILES += \
misc/feedthemonkey.desktop \
misc/feedthemonkey.xpm \
misc/Icon.icns \
README.md \
LICENSE.txt

View file

@ -1,3 +1,4 @@
# FeedTheMonkey # FeedTheMonkey
<img align=right src="http://jeena.net/feedthemonkey/feedthemonkey-icon.png" width='256' alt='Icon'> <img align=right src="http://jeena.net/feedthemonkey/feedthemonkey-icon.png" width='256' alt='Icon'>
@ -6,17 +7,11 @@ FeedTheMonkey is a desktop client for [TinyTinyRSS](http://tt-rss.org). That mea
it doesn't work as a standalone feed reader but only as a client for the TinyTinyRSS API it doesn't work as a standalone feed reader but only as a client for the TinyTinyRSS API
which it uses to get the normalized feeds and to synchronize the "article read" marks. which it uses to get the normalized feeds and to synchronize the "article read" marks.
It is written in C++ with Qt and QML, it also uses Blink to show the contents. You need It is written in Rust and GTK. You need to have an account on a TinyTinyRSS server.
to have Qt 5.6 installed to be able to compile and have a account on a TinyTinyRSS server.
## Installation ## Installation
If you run Linux then there is an AppImage on the [Latest release](https://github.com/jeena/FeedTheMonkey/releases/latest) page. You download it, make executable and are able to run, it should work on most of the distributions out there. TBD
For ArchLinux I package it and it's available on https://aur.archlinux.org/packages/feedthemonkey/
You can compile and install it everywhere Qt is suported, this means on macOS, Windows
and Linux.
## Keyboard shortcuts ## Keyboard shortcuts
@ -33,13 +28,11 @@ The keyboard shortcuts are inspired by other feed readers which are inspired by
`Ctrl -` zoom out `Ctrl -` zoom out
`Ctrl 0` reset zoom `Ctrl 0` reset zoom
On macOS use `Cmd` instead of `Ctrl`.
## Trivia ## Trivia
This is version 2 of FeedTheMonkey, you can find version 1 which was written in PyQt in the v1 branch This is version 3 of FeedTheMonkey, you can find version 1 which was written
of this repo. My goal is to make this usable on many different targets, for now it is only for in PyQt in the v1 branch of this repo and version 2 which was written in C++
the use on a desktop computer but I'd like to see it on a mobile device too. and Qt/QML in the v2 branch.
## Screenshot ## Screenshot
@ -49,7 +42,7 @@ the use on a desktop computer but I'd like to see it on a mobile device too.
This file is part of FeedTheMonkey. This file is part of FeedTheMonkey.
Copyright 2015-2017 Jeena Copyright 2020 Jeena
FeedTheMonkey is free software: you can redistribute it and/or modify FeedTheMonkey is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View file

@ -1,6 +0,0 @@
<RCC>
<qresource prefix="/html">
<file>content.css</file>
<file>content.html</file>
</qresource>
</RCC>

View file

@ -1,7 +0,0 @@
<RCC>
<qresource prefix="/misc">
<file>feedthemonkey.xpm</file>
<file>Icon.icns</file>
<file>feedthemonkey.desktop</file>
</qresource>
</RCC>

View file

@ -1,25 +0,0 @@
# Maintainer: Jeena <hello@jeena.net>
pkgname=feedthemonkey
_name=FeedTheMonkey
pkgver=2.2.0
pkgrel=1
pkgdesc="Desktop client for the TinyTinyRSS reader"
arch=('i686' 'x86_64')
url="http://jabs.nu/feedthemonkey"
license=('GPL3')
depends=('qt5-declarative' 'qt5-quick1' 'qt5-quickcontrols' 'qt5-webengine')
source=("https://github.com/jeena/${_name}/archive/v${pkgver}.tar.gz")
md5sums=('SKIP')
build() {
cd "${_name}-$pkgver"
qmake-qt5 PREFIX=${pkgdir}/usr
make
}
package() {
cd "${_name}-$pkgver"
make install
install -D -m644 COPYING "${pkgdir}/usr/share/licenses/${pkgname}/COPYING"
}

View file

@ -1,51 +0,0 @@
#!/bin/sh
# The macdeployqt app you get while installing Qt is broken for newer Qt
# versions like 5.4 which we use, we will have to replace it.
#
# Download and compile https://github.com/MaximAlien/macdeployqt do not
# use the .dmg which is too old. Move the new macdeployqt so it is in
# $QTDIR/bin/macdeployqt.
#
# Use fixqtlibspath.sh to fix your Qt installation, you need to change the
# path in this script, you don't have to run the Predator part.
#
# Build FeedTheMonkey.app in QtCreator as Release.
if [[ "" == "$QTDIR" ]]; then
QTDIR=~/Qt/5.4/clang_64/
fi
BUILDDIR=$1
APPDIR=$BUILDDIR/FeedTheMonkey.app
CONTENTSDIR=$APPDIR/Contents
ABSPATH=$(cd "$(dirname "$0")"; pwd)
if [[ "" == "$BUILDDIR" ]]; then
echo "Usage: $0 path/to/build/"
exit 1
fi
# libexec
mkdir -p $APPDIR/Contents/libexec
cp $QTDIR/libexec/QtWebProcess $CONTENTSDIR/libexec
cat > $CONTENTSDIR/libexec/qt.conf << EOF
[Paths]
Plugins = ../PlugIns
Qml2Imports = ../Imports/qtquick2
EOF
# lab settings
mkdir -p $CONTENTSDIR/Imports/qtquick2/Qt/labs
cp -R $QTDIR/qml/Qt/labs/settings $CONTENTSDIR/Imports/qtquick2/Qt/labs
cat > $CONTENTSDIR/Resources/qt.conf << EOF
[Paths]
Plugins = PlugIns
Qml2Imports = Imports/qtquick2
EOF
# deploy
$QTDIR/bin/macdeployqt $APPDIR -no-strip -qmldir=$ABSPATH/../../qml -executable=$CONTENTSDIR/libexec/QtWebProcess
open $BUILDDIR

View file

@ -1,116 +0,0 @@
/*
* This file is part of FeedTheMonkey.
*
* Copyright 2015 Jeena
*
* FeedTheMonkey 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 3 of the License, or
* (at your option) any later version.
*
* FeedTheMonkey 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 FeedTheMonkey. If not, see <http://www.gnu.org/licenses/>.
*/
import QtWebEngine 1.8
import QtQuick 2.0
import QtQuick.Controls 1.3
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.3
import TTRSS 1.0
Item {
id: content
property Post post
property ApplicationWindow app
property int textFontSize: 14
property bool nightmode
property int scrollJump: 48
property int pageJump: parent.height
Layout.minimumWidth: 400
onTextFontSizeChanged: webView.setDefaults()
onNightmodeChanged: webView.setDefaults()
function scrollDown(jump) {
if(!jump) {
webView.runJavaScript("window.scrollTo(0, document.body.scrollHeight - " + height + ");")
} else {
webView.runJavaScript("window.scrollBy(0, " + jump + ");")
}
}
function scrollUp(jump) {
if(!jump) {
webView.runJavaScript("window.scrollTo(0, 0);")
} else {
webView.runJavaScript("window.scrollBy(0, -" + jump + ");")
}
}
function loggedOut() {
post = null
}
Label { id: fontLabel }
WebEngineView {
id: webView
anchors.fill: parent
url: "../html/content.html"
property Post post: content.post
function setPost() {
if(post) {
webView.runJavaScript("setArticle(" + post.jsonString + ")")
} else {
webView.runJavaScript("setArticle('logout')")
}
}
function setDefaults() {
// font name needs to be enclosed in single quotes
// and this is needed for El Capitain because ".SF NS Text" won't work
var defFont = ", system, -apple-system, '.SFNSDisplay-Regular', 'Helvetica Neue', 'Lucida Grande'";
var font = "'" + fontLabel.font.family + "'" + defFont;
webView.runJavaScript("document.body.style.fontFamily = \"" + font + "\";");
webView.runJavaScript("document.body.style.fontSize = '" + content.textFontSize + "pt';");
webView.runJavaScript("if(typeof setNightmode == \"function\") setNightmode(" + (content.nightmode ? "true" : "false") + ")")
}
onNavigationRequested: {
if (request.url == "feedthemonkey:previous") {
request.action = WebEngineView.IgnoreRequest;
app.showPreviousPost();
} else if (request.url == "feedthemonkey:next") {
request.action = WebEngineView.IgnoreRequest;
app.showNextPost();
} else if (request.url == "feedthemonkey:open") {
request.action = WebEngineView.IgnoreRequest;
Qt.openUrlExternally(post.link)
} else if (request.navigationType !== WebEngineNavigationRequest.LinkClickedNavigation) {
request.action = WebEngineView.AcceptRequest;
} else {
request.action = WebEngineView.IgnoreRequest;
Qt.openUrlExternally(request.url);
}
}
onLoadingChanged: {
if(!loading) {
setPost()
setDefaults()
}
}
onPostChanged: setPost()
Keys.onPressed: app.keyPressed(event)
}
}

View file

@ -1,84 +0,0 @@
/*
* This file is part of FeedTheMonkey.
*
* Copyright 2015 Jeena
*
* FeedTheMonkey 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 3 of the License, or
* (at your option) any later version.
*
* FeedTheMonkey 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 FeedTheMonkey. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.0
import QtQuick.Controls 1.2
Rectangle {
color: "transparent"
anchors.fill: parent
property string serverUrl: serverUrl.text
property string userName: userName.text
property string password: password.text
Column {
anchors.centerIn: parent
width: parent.width / 2
anchors.margins: parent.width / 4
spacing: 10
Text {
text: qsTr("Please specify a server url, a username and a password.")
wrapMode: Text.WordWrap
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 20
font.pointSize: 20
}
TextField {
id: serverUrl
placeholderText: "http://example.com/ttrss/"
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 20
validator: RegExpValidator { regExp: /https?:\/\/.+/ }
onAccepted: login()
}
TextField {
id: userName
placeholderText: qsTr("username")
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 20
onAccepted: login()
}
TextField {
id: password
placeholderText: qsTr("password")
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 20
echoMode: TextInput.Password
onAccepted: login()
}
Button {
id: loginButton
text: "Ok"
anchors.right: parent.right
anchors.margins: 20
onClicked: login()
}
}
}

View file

@ -1,123 +0,0 @@
/*
* This file is part of FeedTheMonkey.
*
* Copyright 2015 Jeena
*
* FeedTheMonkey 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 3 of the License, or
* (at your option) any later version.
*
* FeedTheMonkey 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 FeedTheMonkey. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.0
import QtQuick.Controls 1.3
Item {
property int textFontSize: 14
property int smallfontSize: 11
property bool nightmode
Component.onCompleted: fixFontSize()
onTextFontSizeChanged: fixFontSize()
function fixFontSize() {
smallfontSize = textFontSize * 0.8
}
id: item
height: d.height + t.height + e.height + 2
Item {
anchors.fill: parent
Item {
anchors.fill: parent
anchors.leftMargin: 15
anchors.rightMargin: 15
anchors.topMargin: 10
anchors.bottomMargin: 10
Column {
id: column
width: parent.width
Item {
width: parent.width
height: d.height
Label {
text: feedTitle
font.pointSize: smallfontSize
textFormat: Text.PlainText
color: nightmode ? "#888" : "gray"
wrapMode: Text.WrapAnywhere
renderType: Text.NativeRendering
elide: Text.ElideLeft
anchors.top: parent.top
anchors.left: parent.left
anchors.right: d.left
maximumLineCount: 1
}
Label {
id: d
text: date.toLocaleString(Qt.locale(), Locale.ShortFormat)
font.pointSize: smallfontSize
textFormat: Text.PlainText
color: nightmode ? "#888" : "gray"
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
renderType: Text.NativeRendering
anchors.right: parent.right
anchors.top: parent.top
}
}
Label {
id: t
text: title
color: nightmode ? (read ? "#888" : "#ddd") : (read ? "gray" : "black")
font.pointSize: textFontSize
textFormat: Text.PlainText
wrapMode: Text.WrapAnywhere
renderType: Text.NativeRendering
width: parent.width
elide: Text.ElideRight
maximumLineCount: 1
}
Label {
id: e
text: excerpt
font.pointSize: smallfontSize
//textFormat: Text.RichText
color: nightmode ? "#888" : "gray"
wrapMode: Text.WrapAnywhere
renderType: Text.NativeRendering
width: parent.width
elide: Text.ElideRight
maximumLineCount: 1
}
}
}
Rectangle {
anchors.top: parent.bottom
width: parent.width
height: 1
color: nightmode ? "#222" : "lightgray"
}
}
MouseArea {
anchors.fill: parent
onClicked: {
parent.parent.parent.currentIndex = index
}
}
}

View file

@ -1,106 +0,0 @@
/*
* This file is part of FeedTheMonkey.
*
* Copyright 2015 Jeena
*
* FeedTheMonkey 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 3 of the License, or
* (at your option) any later version.
*
* FeedTheMonkey 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 FeedTheMonkey. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.0
import TTRSS 1.0
import QtQuick.Controls 1.3
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.3
ScrollView {
id: item
property Server server
property Content content
property Post previousPost
property int textFontSize: 14
property bool nightmode
style: ScrollViewStyle {
transientScrollBars: true
}
function next() {
if(listView.count > listView.currentIndex) {
listView.currentIndex++;
}
}
function previous() {
if(listView.currentIndex > 0) {
listView.currentIndex--;
}
}
onWidthChanged: {
// Hide sidebar if smaller than 200px wide
if(width < 200) {
width = 0;
}
}
Rectangle {
width: 1
color: app.nightmode ? "#111" : "lightgray"
anchors.right: parent.right
anchors.top: parent.top
height: parent.height
}
ListView {
id: listView
focus: true
anchors.fill: parent
spacing: 1
model: item.server.posts
delegate: Component {
PostListItem {
textFontSize: item.textFontSize
nightmode: app.nightmode
width: listView.width
}
}
highlightFollowsCurrentItem: false
highlight: Component {
Rectangle {
width: listView.currentItem.width -1
height: listView.currentItem.height
color: nightmode ? "#15539e" : "lightblue"
y: listView.currentItem.y
}
}
onCurrentItemChanged: {
if(previousPost) {
if(!previousPost.dontChangeRead) {
previousPost.read = true;
} else {
previousPost.dontChangeRead = false;
}
}
item.content.post = server.posts[currentIndex]
previousPost = item.content.post
}
}
}

View file

@ -1,144 +0,0 @@
/*
* This file is part of FeedTheMonkey.
*
* Copyright 2015 Jeena
*
* FeedTheMonkey 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 3 of the License, or
* (at your option) any later version.
*
* FeedTheMonkey 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 FeedTheMonkey. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick.Controls 1.2
import QtQuick.Window 2.0
import QtQuick 2.0
import TTRSS 1.0
MenuBar {
id: menuBar
property bool loggedIn: false
property ServerLogin serverLogin
property Server server
property Sidebar sidebar
property Content content
property bool visible: true
property var oldVisibility
Menu {
visible: menuBar.visible
title: qsTr("File")
MenuItem {
text: qsTr("Close &Window")
shortcut: "Ctrl+W"
onTriggered: Qt.quit()
}
MenuItem {
text: qsTr("Exit")
shortcut: "Ctrl+Q"
onTriggered: Qt.quit()
}
}
Menu {
visible: menuBar.visible
title: qsTr("Action")
MenuItem {
text: qsTr("Reload")
shortcut: "R"
enabled: loggedIn
onTriggered: server.reload()
}
MenuItem {
text: qsTr("Set &Unread")
shortcut: "U"
enabled: loggedIn
onTriggered: {
content.post.dontChangeRead = true
content.post.read = false
}
}
MenuItem {
text: qsTr("Next")
shortcut: "J"
enabled: loggedIn
onTriggered: sidebar.next()
}
MenuItem {
text: qsTr("Previous")
shortcut: "K"
enabled: loggedIn
onTriggered: sidebar.previous()
}
MenuItem {
text: qsTr("Open in Browser")
shortcut: "N"
enabled: loggedIn
onTriggered: Qt.openUrlExternally(content.post.link)
}
MenuItem {
text: qsTr("Log Out")
enabled: loggedIn
onTriggered: serverLogin.logout()
}
}
Menu {
visible: menuBar.visible
title: qsTr("View")
MenuItem {
text: qsTr("Night mode")
shortcut: "1"
onTriggered: app.toggleNightmode()
}
MenuItem {
text: qsTr("Zoom In")
shortcut: "Ctrl++"
enabled: loggedIn
onTriggered: app.zoomIn()
}
MenuItem {
text: qsTr("Zoom Out")
shortcut: "Ctrl+-"
enabled: loggedIn
onTriggered: app.zoomOut()
}
MenuItem {
text: qsTr("Reset")
shortcut: "Ctrl+0"
enabled: loggedIn
onTriggered: app.zoomReset()
}
MenuItem {
text: qsTr("Fullscreen")
shortcut: "F11"
enabled: loggedIn
onTriggered: {
if(app.visibility == Window.FullScreen) {
app.visibility = oldVisibility
} else {
oldVisibility = app.visibility
app.showFullScreen()
}
}
}
}
Menu {
visible: menuBar.visible
title: qsTr("Help")
MenuItem {
text: qsTr("About")
onTriggered: Qt.openUrlExternally("http://jeena.net/feedthemonkey/index.html");
}
}
}

View file

@ -1,254 +0,0 @@
/*
* This file is part of FeedTheMonkey.
*
* Copyright 2015 Jeena
*
* FeedTheMonkey 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 3 of the License, or
* (at your option) any later version.
*
* FeedTheMonkey 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 FeedTheMonkey. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.3
import QtQuick.Controls 1.3
import QtQuick.Window 2.0
import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.1
import Qt.labs.settings 1.0
import TTRSS 1.0
ApplicationWindow {
id: app
title: "FeedTheMonkey"
visible: true
color: nightmode ? "#2d2d2d" : "#eee"
minimumWidth: 480
minimumHeight: 320
width: 800
height: 640
x: 200
y: 200
property Server server: server
property Sidebar sidebar: sidebar
property Content content: content
property variant fontSizes: [7,9,11,13,15,17,19,21,23,25,27,29,31]
property int defaultTextFontSizeIndex: 3
property int textFontSizeIndex: defaultTextFontSizeIndex
property int textFontSize: fontSizes[textFontSizeIndex]
property bool nightmode: false
property bool showMenuBar: false
menuBar: TheMenuBar {
id: menu
serverLogin: serverLogin
server: server
sidebar: sidebar
content: content
visible: app.showMenuBar
__contentItem.visible: visible
}
Settings {
id: settings
category: "window"
property alias x: app.x
property alias y: app.y
property alias width: app.width
property alias height: app.height
property alias sidebarWidth: sidebar.width
property alias textFontSizeIndex: app.textFontSizeIndex
property alias nightmode: app.nightmode
}
function loggedIn() {
if(serverLogin.loggedIn()) {
menu.loggedIn = true;
contentView.visible = true
login.visible = false;
server.initialize(serverLogin.serverUrl, serverLogin.sessionId);
} else {
menu.loggedIn = false
contentView.visible = false
login.visible = true
server.loggedOut()
content.loggedOut()
}
}
function toggleNightmode() {
app.nightmode = !app.nightmode
}
function zoomIn() {
if(textFontSizeIndex + 1 < fontSizes.length) {
textFontSize = fontSizes[++textFontSizeIndex]
}
}
function zoomOut() {
if(textFontSizeIndex - 1 > 0) {
textFontSize = fontSizes[--textFontSizeIndex]
}
}
function zoomReset() {
textFontSizeIndex = defaultTextFontSizeIndex
textFontSize = fontSizes[textFontSizeIndex]
}
function removeHTML(str) {
forEscapingHTML.text = str
return forEscapingHTML.getText(0, forEscapingHTML.length)
}
function showNextPost() {
sidebar.next()
}
function showPreviousPost() {
sidebar.previous()
}
function keyPressed(event) {
switch (event.key) {
case Qt.Key_Right:
case Qt.Key_J:
case Qt.Key_j:
sidebar.next()
break
case Qt.Key_Left:
case Qt.Key_K:
case Qt.Key_k:
sidebar.previous()
break
case Qt.Key_1:
toggleNightmode()
break
case Qt.Key_Home:
content.scrollUp()
break
case Qt.Key_End:
content.scrollDown()
break
case Qt.Key_PageUp:
content.scrollUp(content.pageJump)
break
case Qt.Key_PageDown:
case Qt.Key_Space:
content.scrollDown(content.pageJump)
break
case Qt.Key_Down:
content.scrollDown(content.scrollJump)
break
case Qt.Key_Up:
content.scrollUp(content.scrollJump)
break
case Qt.Key_Enter:
case Qt.Key_Return:
Qt.openUrlExternally(content.post.link)
break
default:
break
}
}
SplitView {
id: contentView
anchors.fill: parent
orientation: Qt.Horizontal
visible: serverLogin.loggedIn()
focus: true
handleDelegate: Rectangle {
width: 1
color: app.nightmode ? "#333" : "#aaa"
}
Sidebar {
id: sidebar
content: content
server: server
implicitWidth: 300
textFontSize: app.textFontSize
nightmode: app.nightmode
}
Content {
id: content
app: app
Layout.minimumWidth: 200
implicitWidth: 624
textFontSize: app.textFontSize
nightmode: app.nightmode
}
Keys.onPressed: keyPressed(event)
Keys.onReleased: {
switch (event.key) {
case Qt.Key_Alt:
app.showMenuBar = !app.showMenuBar
break
default:
break
}
}
}
Login {
id: login
anchors.fill: parent
visible: !serverLogin.loggedIn()
function login() {
serverLogin.login(serverUrl, userName, password)
}
}
MessageDialog {
id: loginErrorAlert
title: "A login error occured"
text: serverLogin.loginError
onAccepted: visible = false
}
ServerLogin {
id: serverLogin
onSessionIdChanged: app.loggedIn()
onLoginErrorChanged: {
console.log("loginError:", loginError)
if(loginError.length > 0) {
loginErrorAlert.visible = true
}
}
}
Server {
id: server
}
TextArea {
id: forEscapingHTML
visible: false
textFormat: TextEdit.RichText
}
Component.onCompleted: {
if(serverLogin.loggedIn()) {
loggedIn();
}
}
}

View file

@ -1,10 +0,0 @@
<RCC>
<qresource prefix="/qml">
<file>main.qml</file>
<file>TheMenuBar.qml</file>
<file>Content.qml</file>
<file>Login.qml</file>
<file>PostListItem.qml</file>
<file>Sidebar.qml</file>
</qresource>
</RCC>

View file

@ -1,49 +0,0 @@
/*
* This file is part of FeedTheMonkey.
*
* Copyright 2015 Jeena
*
* FeedTheMonkey 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 3 of the License, or
* (at your option) any later version.
*
* FeedTheMonkey 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 FeedTheMonkey. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <qdebug.h>
#include <QMetaType>
#include <QtQml>
#include <QIcon>
#include <QtWebEngine/qtwebengineglobal.h>
#include "tinytinyrsslogin.h"
#include "tinytinyrss.h"
#include "post.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
app.setOrganizationName("Jeena");
app.setOrganizationDomain("jeena.net");
app.setApplicationName("FeedTheMonkey");
QtWebEngine::initialize();
qmlRegisterType<TinyTinyRSSLogin>("TTRSS", 1, 0, "ServerLogin");
qmlRegisterType<TinyTinyRSS>("TTRSS", 1, 0, "Server");
qmlRegisterType<Post>("TTRSS", 1, 0, "Post");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml")));
return app.exec();
}

3
src/main.rs Normal file
View file

@ -0,0 +1,3 @@
fn main() {
println!("Hello, world!");
}

View file

@ -1,79 +0,0 @@
/*
* This file is part of FeedTheMonkey.
*
* Copyright 2015 Jeena
*
* FeedTheMonkey 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 3 of the License, or
* (at your option) any later version.
*
* FeedTheMonkey 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 FeedTheMonkey. If not, see <http://www.gnu.org/licenses/>.
*/
#include "post.h"
#include <QDebug>
#include <QJsonDocument>
#include <QTextDocument>
Post::Post(QObject *parent) : QObject(parent)
{
}
Post::Post(QJsonObject post, QObject *parent) : QObject(parent)
{
mTitle = html2text(post.value("title").toString().trimmed());
mFeedTitle = html2text(post.value("feed_title").toString().trimmed());
mId = post.value("id").toInt();
mFeedId = post.value("feed_id").toString().trimmed();
mAuthor = post.value("author").toString().trimmed();
QUrl url(post.value("link").toString().trimmed());
mLink = url;
QDateTime timestamp;
timestamp.setTime_t(post.value("updated").toInt());
mDate = timestamp;
mContent = post.value("content").toString().trimmed();
mExcerpt = html2text(post.value("excerpt").toString().remove(QRegExp("<[^>]*>")).replace("&hellip;", " ...").trimmed().replace("(\\s+)", " ").replace("\n", ""));
mStarred = post.value("marked").toBool();
mRead = !post.value("unread").toBool();
mDontChangeRead = false;
QJsonDocument doc(post);
QString result(doc.toJson(QJsonDocument::Indented));
mJsonString = result;
}
Post::~Post()
{
}
void Post::setRead(bool r)
{
if(mRead == r) return;
mRead = r;
emit readChanged(mRead);
}
void Post::setDontChangeRead(bool r)
{
if(mDontChangeRead == r) return;
mDontChangeRead = r;
emit dontChangeReadChanged(mDontChangeRead);
}
QString Post::html2text(const QString htmlString)
{
QTextDocument doc;
doc.setHtml(htmlString);
return doc.toPlainText();
}

View file

@ -1,89 +0,0 @@
/*
* This file is part of FeedTheMonkey.
*
* Copyright 2015 Jeena
*
* FeedTheMonkey 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 3 of the License, or
* (at your option) any later version.
*
* FeedTheMonkey 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 FeedTheMonkey. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef POST_H
#define POST_H
#include <QObject>
#include <QUrl>
#include <QDate>
#include <QJsonObject>
class Post : public QObject
{
Q_OBJECT
Q_PROPERTY(QString title READ title CONSTANT)
Q_PROPERTY(QString feedTitle READ feedTitle CONSTANT)
Q_PROPERTY(int id READ id CONSTANT)
Q_PROPERTY(QString feedId READ feedId CONSTANT)
Q_PROPERTY(QString author READ author CONSTANT)
Q_PROPERTY(QUrl link READ link CONSTANT)
Q_PROPERTY(QDateTime date READ date CONSTANT)
Q_PROPERTY(QString content READ content CONSTANT)
Q_PROPERTY(QString excerpt READ excerpt CONSTANT)
Q_PROPERTY(bool starred READ starred NOTIFY starredChanged)
Q_PROPERTY(bool read READ read WRITE setRead NOTIFY readChanged)
Q_PROPERTY(bool dontChangeRead READ dontChangeRead WRITE setDontChangeRead NOTIFY dontChangeReadChanged)
Q_PROPERTY(QString jsonString READ jsonString CONSTANT)
public:
Post(QObject *parent = 0);
Post(QJsonObject post, QObject *parent = 0);
~Post();
QString title() const { return mTitle; }
QString feedTitle() const { return mFeedTitle; }
int id() const { return mId; }
QString feedId() const { return mFeedId; }
QString author() const { return mAuthor; }
QUrl link() const { return mLink; }
QDateTime date() const { return mDate; }
QString content() const { return mContent; }
QString excerpt() const { return mExcerpt; }
bool starred() const { return mStarred; }
bool read() { return mRead; }
void setRead(bool r);
bool dontChangeRead() const { return mDontChangeRead; }
void setDontChangeRead(bool r);
QString jsonString() const { return mJsonString; }
signals:
void starredChanged(bool);
void readChanged(bool);
void dontChangeReadChanged(bool);
public slots:
private:
QString mTitle;
QString mFeedTitle;
int mId;
QString mFeedId;
QString mAuthor;
QUrl mLink;
QDateTime mDate;
QString mContent;
QString mExcerpt;
bool mStarred;
bool mRead;
bool mDontChangeRead;
QString mJsonString;
QString html2text(const QString htmlString);
};
#endif // POST_H

View file

@ -1,150 +0,0 @@
/*
* This file is part of FeedTheMonkey.
*
* Copyright 2015 Jeena
*
* FeedTheMonkey 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 3 of the License, or
* (at your option) any later version.
*
* FeedTheMonkey 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 FeedTheMonkey. If not, see <http://www.gnu.org/licenses/>.
*/
#include "tinytinyrss.h"
#include <QJsonDocument>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QJsonArray>
TinyTinyRSS::TinyTinyRSS(QObject *parent) :
QObject(parent)
{
qRegisterMetaType<QList<Post *> >();
mNetworkManager = new QNetworkAccessManager(this);
mPosts = QList<Post *>();
}
TinyTinyRSS::~TinyTinyRSS()
{
mPosts.clear();
delete mNetworkManager;
}
void TinyTinyRSS::initialize(const QString serverUrl, const QString sessionId)
{
mServerUrl = serverUrl;
mSessionId = sessionId;
reload();
}
void TinyTinyRSS::reload()
{
QVariantMap opts;
opts.insert("show_excerpt", false);
opts.insert("view_mode", "unread");
opts.insert("show_content", true);
opts.insert("feed_id", -4);
opts.insert("skip", 0);
doOperation("getHeadlines", opts, [this] (const QJsonObject &json) {
mPosts.clear();
QJsonArray posts = json.value("content").toArray();
for(int i = 0; i < posts.count(); i++)
{
QJsonObject postJson = posts.at(i).toObject();
Post *post = new Post(postJson, this);
connect(post, SIGNAL(readChanged(bool)), this, SLOT(onPostReadChanged(bool)));
mPosts.append(post);
}
emit postsChanged(mPosts);
});
}
void TinyTinyRSS::loggedOut()
{
mServerUrl = nullptr;
mSessionId = nullptr;
mPosts.clear();
emit postsChanged(mPosts);
}
void TinyTinyRSS::doOperation(QString operation, QVariantMap opts, std::function<void (const QJsonObject &json)> callback)
{
QVariantMap options;
options.insert("sid", mSessionId);
options.insert("op", operation);
QMapIterator<QString, QVariant> i(opts);
while (i.hasNext()) {
i.next();
options.insert(i.key(), i.value());
}
QJsonObject jsonobj = QJsonObject::fromVariantMap(options);
QJsonDocument json = QJsonDocument(jsonobj);
QNetworkRequest request(mServerUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QNetworkReply *reply = mNetworkManager->post(request, json.toJson());
connect(reply, &QNetworkReply::finished, [callback, reply] () {
if (reply) {
if (reply->error() == QNetworkReply::NoError) {
QString jsonString = QString(reply->readAll());
QJsonDocument json = QJsonDocument::fromJson(jsonString.toUtf8());
callback(json.object());
} else {
int httpStatus = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
//do some error management
qWarning() << "HTTP error: " << httpStatus;
}
reply->deleteLater();
}
});
}
void TinyTinyRSS::onPostReadChanged(bool r)
{
Post *post = (Post *)sender();
updateArticle(post->id(), 2, !r, [post] (const QJsonObject &) {
// not doing anything with this yet.
});
}
void TinyTinyRSS::updateArticle(int articleId, int field, bool trueFalse, std::function<void (const QJsonObject &json)> callback)
{
QVariantMap opts;
opts.insert("article_ids", articleId);
opts.insert("field", field);
opts.insert("mode", trueFalse ? 1 : 0);
doOperation("updateArticle", opts, callback);
}
QQmlListProperty<Post> TinyTinyRSS::posts()
{
return QQmlListProperty<Post>(this, mPosts);
}
int TinyTinyRSS::postsCount() const
{
return mPosts.count();
}
Post *TinyTinyRSS::post(int index) const
{
return mPosts.at(index);
}

View file

@ -1,68 +0,0 @@
/*
* This file is part of FeedTheMonkey.
*
* Copyright 2015 Jeena
*
* FeedTheMonkey 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 3 of the License, or
* (at your option) any later version.
*
* FeedTheMonkey 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 FeedTheMonkey. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TINYTINYRSS_H
#define TINYTINYRSS_H
#include <QObject>
#include <QMap>
#include <QNetworkReply>
#include <QList>
#include <QQmlListProperty>
#include <QJsonObject>
#include <functional>
#include "post.h"
class TinyTinyRSS : public QObject
{
Q_OBJECT
Q_PROPERTY(QQmlListProperty<Post> posts READ posts NOTIFY postsChanged)
public:
TinyTinyRSS(QObject *parent = 0);
~TinyTinyRSS();
Q_INVOKABLE void initialize(const QString serverUrl, const QString sessionId);
Q_INVOKABLE void reload();
Q_INVOKABLE void loggedOut();
QQmlListProperty<Post> posts();
int postsCount() const;
Post *post(int) const;
signals:
void postsChanged(QList<Post *>);
private slots:
void onPostReadChanged(bool);
private:
void doOperation(QString operation, QVariantMap opts, std::function<void (const QJsonObject &json)> callback);
void updateArticle(int articleId, int field, bool trueFalse, std::function<void (const QJsonObject &json)> callback);
QString mServerUrl;
QString mSessionId;
QList<Post*> mPosts;
QNetworkAccessManager *mNetworkManager;
};
#endif // TINYTINYRSS_H

View file

@ -1,135 +0,0 @@
/*
* This file is part of FeedTheMonkey.
*
* Copyright 2015 Jeena
*
* FeedTheMonkey 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 3 of the License, or
* (at your option) any later version.
*
* FeedTheMonkey 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 FeedTheMonkey. If not, see <http://www.gnu.org/licenses/>.
*/
#include "tinytinyrsslogin.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkReply>
#include <QSettings>
#define APP_URL "net.jeena"
#define APP_NAME "FeedTheMonkey"
TinyTinyRSSLogin::TinyTinyRSSLogin(QObject *parent) :
QObject(parent)
{
mNetworkManager = new QNetworkAccessManager(this);
QSettings settings;
mSessionId = settings.value("sessionId").toString();
mServerUrl = settings.value("serverUrl").toString();
}
TinyTinyRSSLogin::~TinyTinyRSSLogin()
{
delete mNetworkManager;
}
bool TinyTinyRSSLogin::loggedIn()
{
return !mSessionId.isEmpty();
}
void TinyTinyRSSLogin::login(const QString serverUrl, const QString user, const QString password)
{
mServerUrl = QUrl(serverUrl + "/api/");
QVariantMap options;
options.insert("op", "login");
options.insert("user", user);
options.insert("password", password);
QJsonObject jsonobj = QJsonObject::fromVariantMap(options);
QJsonDocument json = QJsonDocument(jsonobj);
QNetworkRequest request(mServerUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QNetworkReply *reply = mNetworkManager->post(request, json.toJson());
connect(reply, SIGNAL(finished()), this, SLOT(reply()));
}
void TinyTinyRSSLogin::logout()
{
if(mSessionId.length() > 0 && mServerUrl.toString().length() > 0) {
QVariantMap options;
options.insert("op", "logout");
options.insert("sid", mSessionId);
QJsonObject jsonobj = QJsonObject::fromVariantMap(options);
QJsonDocument json = QJsonDocument(jsonobj);
QNetworkRequest request(mServerUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QNetworkReply *reply = mNetworkManager->post(request, json.toJson());
connect(reply, SIGNAL(finished()), this, SLOT(reply()));
}
}
void TinyTinyRSSLogin::reply()
{
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
if (reply) {
if (reply->error() == QNetworkReply::NoError) {
QString jsonString = QString(reply->readAll());
QJsonDocument json = QJsonDocument::fromJson(jsonString.toUtf8());
if(json.object().value("content").toObject().value("error").toString().length() > 0) {
mLoginError = json.object().value("content").toObject().value("error").toString();
qWarning() << mLoginError;
emit loginErrorChanged(mLoginError);
if(mLoginError == "NOT_LOGGED_IN") {
mSessionId = nullptr;
mServerUrl = nullptr;
QSettings settings;
settings.remove("sessionId");
settings.remove("serverUrl");
settings.sync();
emit sessionIdChanged(mSessionId);
}
} else {
mSessionId = json.object().value("content").toObject().value("session_id").toString();
emit sessionIdChanged(mSessionId);
QSettings settings;
settings.setValue("sessionId", mSessionId);
settings.setValue("serverUrl", mServerUrl);
settings.sync();
}
} else {
mLoginError = "HTTP error: "
+ reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toString()
+ " :: "
+ reply->errorString();
qWarning() << mLoginError;
emit loginErrorChanged(mLoginError);
}
reply->deleteLater();
}
}

View file

@ -1,60 +0,0 @@
/*
* This file is part of FeedTheMonkey.
*
* Copyright 2015 Jeena
*
* FeedTheMonkey 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 3 of the License, or
* (at your option) any later version.
*
* FeedTheMonkey 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 FeedTheMonkey. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TINYTINYRSSLOGIN_H
#define TINYTINYRSSLOGIN_H
#include <QObject>
#include <QMetaType>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
class TinyTinyRSSLogin : public QObject
{
Q_OBJECT
Q_PROPERTY(QString sessionId READ sessionId NOTIFY sessionIdChanged)
Q_PROPERTY(QUrl serverUrl READ serverUrl)
Q_PROPERTY(QString loginError READ loginError NOTIFY loginErrorChanged)
public:
TinyTinyRSSLogin(QObject *parent = 0);
~TinyTinyRSSLogin();
QString sessionId() const { return mSessionId; }
QUrl serverUrl() const { return mServerUrl; }
QString loginError() const { return mLoginError; }
Q_INVOKABLE bool loggedIn();
Q_INVOKABLE void login(const QString serverUrl, const QString user, const QString password);
Q_INVOKABLE void logout();
signals:
void sessionIdChanged(QString);
void loginErrorChanged(QString);
private slots:
void reply();
private:
QString mSessionId;
QUrl mServerUrl;
QString mLoginError;
QNetworkAccessManager *mNetworkManager;
};
#endif // TINYTINYRSSLOGIN_H