Pocket integration
BIN
sailfish/images/z1.0/icon-m-bad-selected.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
sailfish/images/z1.0/icon-m-bad.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
sailfish/images/z1.0/icon-m-good-selected.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
sailfish/images/z1.0/icon-m-good.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
sailfish/images/z1.0/icon-m-pocket.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.6 KiB |
BIN
sailfish/images/z1.25/icon-m-bad-selected.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
sailfish/images/z1.25/icon-m-bad.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
sailfish/images/z1.25/icon-m-good-selected.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
sailfish/images/z1.25/icon-m-good.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
sailfish/images/z1.25/icon-m-pocket.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 2.1 KiB |
BIN
sailfish/images/z1.5-large/icon-m-bad-selected.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
sailfish/images/z1.5-large/icon-m-bad.png
Normal file
|
After Width: | Height: | Size: 2 KiB |
BIN
sailfish/images/z1.5-large/icon-m-good-selected.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
sailfish/images/z1.5-large/icon-m-good.png
Normal file
|
After Width: | Height: | Size: 2 KiB |
BIN
sailfish/images/z1.5-large/icon-m-pocket.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.8 KiB |
BIN
sailfish/images/z1.5/icon-m-bad-selected.png
Normal file
|
After Width: | Height: | Size: 2 KiB |
BIN
sailfish/images/z1.5/icon-m-bad.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
sailfish/images/z1.5/icon-m-good-selected.png
Normal file
|
After Width: | Height: | Size: 2 KiB |
BIN
sailfish/images/z1.5/icon-m-good.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
sailfish/images/z1.5/icon-m-pocket.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.5 KiB |
BIN
sailfish/images/z1.75/icon-m-bad-selected.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
sailfish/images/z1.75/icon-m-bad.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
sailfish/images/z1.75/icon-m-good-selected.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
sailfish/images/z1.75/icon-m-good.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
sailfish/images/z1.75/icon-m-pocket.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.9 KiB |
BIN
sailfish/images/z2.0/icon-m-bad-selected.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
sailfish/images/z2.0/icon-m-bad.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
sailfish/images/z2.0/icon-m-good-selected.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
sailfish/images/z2.0/icon-m-good.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
sailfish/images/z2.0/icon-m-pocket.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3.5 KiB |
|
|
@ -1 +1,4 @@
|
|||
#include <QString>
|
||||
|
||||
static const quint64 KEY = Q_UINT64_C(0x00000000000000000);
|
||||
static const QString pocket_consumer_key = "";
|
||||
|
|
|
|||
59
sailfish/qml/ButtonItem.qml
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
Copyright (C) 2017 Michal Kosciesza <michal@mkiol.net>
|
||||
|
||||
This file is part of Kaktus.
|
||||
|
||||
Kaktus 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.
|
||||
|
||||
Kaktus 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 Kaktus. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick 2.0
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property alias button: _button
|
||||
property alias description: desc.text
|
||||
|
||||
opacity: enabled ? 1.0 : 0.4
|
||||
height: col.height + 5*Theme.paddingSmall
|
||||
|
||||
anchors {left: parent.left; right: parent.right}
|
||||
|
||||
Column {
|
||||
id: col
|
||||
|
||||
spacing: Theme.paddingSmall
|
||||
|
||||
anchors {
|
||||
top: parent.top; topMargin: 3*Theme.paddingSmall
|
||||
left: parent.left; right: parent.right
|
||||
leftMargin: Theme.horizontalPageMargin; rightMargin: Theme.horizontalPageMargin
|
||||
}
|
||||
|
||||
Button {
|
||||
id: _button
|
||||
enabled: root.enabled
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
Label {
|
||||
id: desc
|
||||
anchors { left: parent.left; right: parent.right }
|
||||
wrapMode: Text.Wrap
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
color: Theme.secondaryColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -38,17 +38,58 @@ Page {
|
|||
|
||||
ActiveDetector {}
|
||||
|
||||
SilicaListView {
|
||||
anchors { top: parent.top; left: parent.left; right: parent.right }
|
||||
clip: true
|
||||
|
||||
height: app.flickHeight
|
||||
|
||||
header: PageHeader {
|
||||
title: qsTr("Changelog")
|
||||
SilicaFlickable {
|
||||
id: flick
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
}
|
||||
height: app.flickHeight
|
||||
clip: true
|
||||
contentHeight: content.height
|
||||
|
||||
model: VisualItemModel {
|
||||
Column {
|
||||
id: content
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
spacing: Theme.paddingMedium
|
||||
|
||||
PageHeader {
|
||||
title: qsTr("Changelog")
|
||||
}
|
||||
|
||||
SectionHeader {
|
||||
text: qsTr("Version %1").arg("2.6.0")
|
||||
}
|
||||
|
||||
LogItem {
|
||||
title: "Pocket integration"
|
||||
description: "Pocket is an Internet tool for saving articles to read later. Integration implemented in Kaktus provides \"Add to Pocket\" button in the articles list and in the web viewer.";
|
||||
}
|
||||
|
||||
LogItem {
|
||||
title: "Share link"
|
||||
description: "\"Share link\" button has been added. Due to Jolla Store restrictions it will be enabled only in OpenRepos package.";
|
||||
}
|
||||
|
||||
LogItem {
|
||||
title: "Improved app icon"
|
||||
description: "Kaktus icon has a new fresh look!"
|
||||
}
|
||||
|
||||
LogItem {
|
||||
title: "Delete web viewer cookies"
|
||||
description: "Option in the settings that allows you to clear cache and cookies of the web viewer."
|
||||
}
|
||||
|
||||
LogItem {
|
||||
title: "Spanish translation update"
|
||||
description: "Spanish translations has been updated."
|
||||
}
|
||||
|
||||
SectionHeader {
|
||||
text: qsTr("Version %1").arg("2.5.3")
|
||||
|
|
@ -282,13 +323,11 @@ Page {
|
|||
}*/
|
||||
|
||||
|
||||
Item {
|
||||
height: Theme.paddingMedium
|
||||
}
|
||||
|
||||
Spacer {}
|
||||
}
|
||||
|
||||
VerticalScrollDecorator {}
|
||||
}
|
||||
|
||||
VerticalScrollDecorator {
|
||||
flickable: flick
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,6 @@ Item {
|
|||
}
|
||||
|
||||
onBusyChanged: {
|
||||
//console.log("onBusyChanged:", busy)
|
||||
open = busy
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,6 +58,8 @@ ListItem {
|
|||
readonly property alias expandable: box.expandable
|
||||
property bool expandedMode: settings.expandedMode
|
||||
|
||||
property int evaluation: 0
|
||||
|
||||
signal markedAsRead
|
||||
signal markedAsUnread
|
||||
signal markedReadlater
|
||||
|
|
@ -70,6 +72,9 @@ ListItem {
|
|||
signal openInViewer
|
||||
signal openInBrowser
|
||||
signal showFeedContent
|
||||
signal evaluated(int evaluation)
|
||||
signal share
|
||||
signal pocketAdd
|
||||
|
||||
enabled: !last && !daterow
|
||||
|
||||
|
|
@ -80,7 +85,7 @@ ListItem {
|
|||
|
||||
onMenuOpenChanged: { if(menuOpen) app.hideBar() }
|
||||
|
||||
menu: last ? null : settings.iconContextMenu ? iconContextMenu : contextMenu
|
||||
menu: last ? null : iconContextMenu
|
||||
|
||||
onHiddenChanged: {
|
||||
if (hidden && expanded) {
|
||||
|
|
@ -134,7 +139,7 @@ ListItem {
|
|||
}
|
||||
}*/
|
||||
|
||||
BackgroundItem {
|
||||
/*BackgroundItem {
|
||||
id: star
|
||||
anchors.right: parent.right; anchors.top: parent.top
|
||||
height: Theme.iconSizeSmall + 2*Theme.paddingMedium
|
||||
|
|
@ -164,6 +169,22 @@ ListItem {
|
|||
return "image://theme/icon-m-favorite?"+Theme.primaryColor;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
SmallIconButton {
|
||||
id: star
|
||||
anchors.right: parent.right; anchors.top: parent.top
|
||||
marginV: 2*Theme.paddingMedium
|
||||
visible: !last && !daterow
|
||||
|
||||
icon.source: root.readlater ? "image://theme/icon-m-favorite-selected":
|
||||
"image://theme/icon-m-favorite"
|
||||
onClicked: {
|
||||
if (root.readlater)
|
||||
root.unmarkedReadlater()
|
||||
else
|
||||
root.markedReadlater()
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
|
|
@ -326,7 +347,7 @@ ListItem {
|
|||
|
||||
Image {
|
||||
id: expanderIcon
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.verticalCenter: expanderLabel.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Theme.paddingMedium
|
||||
source: "image://theme/icon-lock-more"
|
||||
|
|
@ -382,7 +403,8 @@ ListItem {
|
|||
|
||||
|
||||
Label {
|
||||
anchors.left: parent.left; anchors.right: parent.right
|
||||
id: authorLabel
|
||||
anchors {left: parent.left; right: parent.right}
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
color: root.down ? Theme.secondaryHighlightColor : Theme.secondaryColor
|
||||
truncationMode: TruncationMode.Fade
|
||||
|
|
@ -390,6 +412,53 @@ ListItem {
|
|||
? utils.getHumanFriendlyTimeString(date)+" • "+root.author
|
||||
: utils.getHumanFriendlyTimeString(date)
|
||||
}
|
||||
|
||||
/*Item {
|
||||
anchors.left: parent.left; anchors.right: parent.right
|
||||
height: evaluationButtons.height
|
||||
|
||||
Label {
|
||||
id: authorLabel
|
||||
anchors {left: parent.left; right: evaluationButtons.left; verticalCenter: parent.verticalCenter}
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
color: root.down ? Theme.secondaryHighlightColor : Theme.secondaryColor
|
||||
truncationMode: TruncationMode.Fade
|
||||
text: root.author!=""
|
||||
? utils.getHumanFriendlyTimeString(date)+" • "+root.author
|
||||
: utils.getHumanFriendlyTimeString(date)
|
||||
}
|
||||
|
||||
Row {
|
||||
id: evaluationButtons
|
||||
anchors {right: parent.right; verticalCenter: parent.verticalCenter}
|
||||
|
||||
SmallIconButton {
|
||||
icon.source: root.evaluation === 1 ?
|
||||
"image://icons/icon-m-good-selected" :
|
||||
"image://icons/icon-m-good"
|
||||
onClicked: {
|
||||
if (root.evaluation === 1)
|
||||
root.evaluation = 0
|
||||
else
|
||||
root.evaluation = 1
|
||||
evaluated(root.evaluation)
|
||||
}
|
||||
}
|
||||
|
||||
SmallIconButton {
|
||||
icon.source: root.evaluation === -1 ?
|
||||
"image://icons/icon-m-bad-selected" :
|
||||
"image://icons/icon-m-bad"
|
||||
onClicked: {
|
||||
if (root.evaluation === -1)
|
||||
root.evaluation = 0
|
||||
else
|
||||
root.evaluation = -1
|
||||
evaluated(root.evaluation)
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
|
|
@ -476,143 +545,56 @@ ListItem {
|
|||
visible: enabled
|
||||
//enabled: settings.clickBehavior !== 2
|
||||
onClicked: {
|
||||
root.showFeedContent();
|
||||
root.expanded = false;
|
||||
menu.hide();
|
||||
root.showFeedContent()
|
||||
root.expanded = false
|
||||
menu.hide()
|
||||
}
|
||||
}
|
||||
|
||||
IconMenuItem {
|
||||
text: qsTr("Add to Pocket")
|
||||
visible: settings.pocketEnabled
|
||||
enabled: settings.pocketEnabled && dm.online
|
||||
icon.source: "image://icons/icon-m-pocket"
|
||||
busy: pocket.busy
|
||||
onClicked: root.pocketAdd()
|
||||
}
|
||||
|
||||
IconMenuItem {
|
||||
text: qsTr("Share link")
|
||||
icon.source: "image://theme/icon-m-share"
|
||||
onClicked: root.share()
|
||||
}
|
||||
|
||||
IconMenuItem {
|
||||
id: likeItem
|
||||
text: qsTr("Toggle Like")
|
||||
icon.source: root.liked ? 'image://icons/icon-m-like-selected' : 'image://icons/icon-m-like'
|
||||
icon.source: root.liked ? "image://icons/icon-m-like-selected" : "image://icons/icon-m-like"
|
||||
enabled: settings.showBroadcast && app.isOldReader
|
||||
visible: enabled
|
||||
onClicked: {
|
||||
if (root.liked) {
|
||||
root.unmarkedLike();
|
||||
root.unmarkedLike()
|
||||
} else {
|
||||
root.markedLike();
|
||||
root.markedLike()
|
||||
}
|
||||
menu.hide();
|
||||
menu.hide()
|
||||
}
|
||||
}
|
||||
|
||||
IconMenuItem {
|
||||
text: qsTr("Toggle Share")
|
||||
icon.source: root.broadcast ? 'image://icons/icon-m-share-selected' : 'image://icons/icon-m-share'
|
||||
icon.source: root.broadcast ? "image://icons/icon-m-share-selected" : "image://icons/icon-m-share"
|
||||
enabled: settings.showBroadcast && app.isOldReader &&
|
||||
!root.friendStream
|
||||
visible: enabled
|
||||
onClicked: {
|
||||
if (root.broadcast) {
|
||||
root.unmarkedBroadcast();
|
||||
root.unmarkedBroadcast()
|
||||
} else {
|
||||
root.markedBroadcast();
|
||||
root.markedBroadcast()
|
||||
}
|
||||
menu.hide();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: contextMenu
|
||||
ContextMenu {
|
||||
|
||||
MenuItem {
|
||||
text: read ? qsTr("Mark as unread") : qsTr("Mark as read")
|
||||
visible: enabled
|
||||
enabled: root.showMarkedAsRead
|
||||
onClicked: {
|
||||
if (read) {
|
||||
root.markedAsUnread();
|
||||
} else {
|
||||
root.markedAsRead();
|
||||
root.expanded = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: qsTr("Mark above as read")
|
||||
visible: enabled
|
||||
enabled: root.showMarkedAsRead && index > 1
|
||||
onClicked: {
|
||||
root.markedAboveAsRead();
|
||||
root.expanded = false;
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: app.isNetvibes || app.isFeedly ?
|
||||
readlater ? qsTr("Unsave") : qsTr("Save") :
|
||||
readlater ? qsTr("Unstar") : qsTr("Star")
|
||||
onClicked: {
|
||||
if (readlater) {
|
||||
root.unmarkedReadlater();
|
||||
} else {
|
||||
root.markedReadlater();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: liked ? qsTr("Unlike") : qsTr("Like")
|
||||
enabled: settings.showBroadcast && app.isOldReader
|
||||
visible: enabled
|
||||
onClicked: {
|
||||
if (liked) {
|
||||
root.unmarkedLike();
|
||||
} else {
|
||||
root.markedLike();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: broadcast ? qsTr("Unshare") : qsTr("Share with followers")
|
||||
enabled: settings.showBroadcast && app.isOldReader &&
|
||||
!root.friendStream
|
||||
visible: enabled
|
||||
onClicked: {
|
||||
if (broadcast) {
|
||||
root.unmarkedBroadcast();
|
||||
} else {
|
||||
root.markedBroadcast();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: qsTr("Open in viewer")
|
||||
visible: enabled
|
||||
//enabled: settings.clickBehavior !== 0
|
||||
onClicked: {
|
||||
root.openInViewer();
|
||||
root.expanded = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MenuItem {
|
||||
text: qsTr("Open in browser")
|
||||
visible: enabled
|
||||
//enabled: settings.clickBehavior !== 1
|
||||
onClicked: {
|
||||
root.openInBrowser();
|
||||
root.expanded = false;
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: qsTr("Show feed content")
|
||||
visible: enabled
|
||||
//enabled: settings.clickBehavior !== 2
|
||||
onClicked: {
|
||||
root.showFeedContent();
|
||||
root.expanded = false;
|
||||
menu.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -248,6 +248,7 @@ Page {
|
|||
landscapeMode: root.landscapeMode
|
||||
onlineurl: model.link
|
||||
offlineurl: cache.getUrlbyId(model.uid)
|
||||
evaluation: ai.evaluation(model.uid)
|
||||
|
||||
signal singleEntryClicked
|
||||
signal doubleEntryClicked
|
||||
|
|
@ -431,6 +432,10 @@ Page {
|
|||
|
||||
onMarkedReadlater: {
|
||||
entryModel.setData(index, "readlater", 1, "");
|
||||
if (evaluation !== -1) {
|
||||
evaluation = 1
|
||||
ai.addEvaluation(model.uid, model.title, evaluation)
|
||||
}
|
||||
}
|
||||
|
||||
onUnmarkedReadlater: {
|
||||
|
|
@ -464,18 +469,30 @@ Page {
|
|||
|
||||
onOpenInBrowser: {
|
||||
if (!check()) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
openInExaternalBrowser(model.index, model.link, model.uid);
|
||||
openInExaternalBrowser(model.index, model.link, model.uid)
|
||||
}
|
||||
|
||||
onOpenInViewer: {
|
||||
if (!check()) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
openEntryInViewer();
|
||||
openEntryInViewer()
|
||||
}
|
||||
|
||||
onEvaluated: {
|
||||
ai.addEvaluation(model.uid, model.title, evaluation)
|
||||
}
|
||||
|
||||
onShare: {
|
||||
pageStack.push(Qt.resolvedUrl("ShareLinkPage.qml"),{"link": model.link, "linkTitle": model.title})
|
||||
}
|
||||
|
||||
onPocketAdd: {
|
||||
pocket.add(model.link, model.title)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -80,8 +80,15 @@ Page {
|
|||
Connections {
|
||||
target: Qt.application
|
||||
onActiveChanged: {
|
||||
if(!Qt.application.active && settings.powerSaveMode) {
|
||||
pageStack.pop();
|
||||
if(!Qt.application.active) {
|
||||
if (settings.powerSaveMode && root.status === PageStatus.Active) {
|
||||
pageStack.pop()
|
||||
return
|
||||
}
|
||||
if (root.status !== PageStatus.Active) {
|
||||
pageStack.pop(pageStack.previousPage(root), PageStackAction.Immediate)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -95,6 +102,10 @@ Page {
|
|||
}
|
||||
}
|
||||
|
||||
function share() {
|
||||
pageStack.push(Qt.resolvedUrl("ShareLinkPage.qml"),{"link": root.onlineUrl, "linkTitle": root.title});
|
||||
}
|
||||
|
||||
function check() {
|
||||
// Not allowed while Syncing
|
||||
if (dm.busy || fetcher.busy || dm.removerBusy) {
|
||||
|
|
@ -409,6 +420,23 @@ Page {
|
|||
}
|
||||
}
|
||||
|
||||
IconMenuItem {
|
||||
text: qsTr("Add to Pocket")
|
||||
visible: settings.pocketEnabled
|
||||
enabled: settings.pocketEnabled && dm.online
|
||||
icon.source: "image://icons/icon-m-pocket"
|
||||
busy: pocket.busy
|
||||
onClicked: {
|
||||
pocket.add(root.onlineUrl, root.title)
|
||||
}
|
||||
}
|
||||
|
||||
IconMenuItem {
|
||||
text: qsTr("Share link")
|
||||
icon.source: "image://theme/icon-m-share"
|
||||
onClicked: root.share()
|
||||
}
|
||||
|
||||
IconBarItem {
|
||||
text: qsTr("Toggle Like")
|
||||
icon: root.liked ? "image://icons/icon-m-like-selected" : "image://icons/icon-m-like"
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ Item {
|
|||
property alias icon: iconButton.icon
|
||||
property alias enabled: iconButton.enabled
|
||||
property alias text: lbl.text
|
||||
property bool busy: false
|
||||
|
||||
width: iconButton.width
|
||||
height: iconButton.height
|
||||
|
|
@ -50,6 +51,21 @@ Item {
|
|||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
opacity: root.busy ? 1.0 : 0.0
|
||||
visible: opacity > 0.0
|
||||
Behavior on opacity { FadeAnimation {} }
|
||||
source: "image://theme/graphic-busyindicator-medium"
|
||||
RotationAnimation on rotation {
|
||||
loops: Animation.Infinite
|
||||
from: 0
|
||||
to: 360
|
||||
duration: 1200
|
||||
running: root.busy && Qt.application.active
|
||||
}
|
||||
}
|
||||
|
||||
IconButton {
|
||||
id: iconButton
|
||||
onClicked: root.clicked();
|
||||
|
|
|
|||
194
sailfish/qml/Pocket.qml
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
Copyright (C) 2017 Michal Kosciesza <michal@mkiol.net>
|
||||
|
||||
This file is part of Kaktus.
|
||||
|
||||
Kaktus 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.
|
||||
|
||||
Kaktus 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 Kaktus. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick 2.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property bool busy: false
|
||||
|
||||
readonly property string consumer_key: settings.pocketConsumerKey()
|
||||
readonly property string redirect_uri: "kaktus:authorizationFinished"
|
||||
readonly property string request_url: "https://getpocket.com/v3/oauth/request"
|
||||
readonly property string authorize_url: "https://getpocket.com/auth/authorize"
|
||||
readonly property string oauth_authorize_url: "https://getpocket.com/v3/oauth/authorize"
|
||||
readonly property string add_url: "https://getpocket.com/v3/add"
|
||||
|
||||
property string request_token
|
||||
property string access_token: settings.pocketToken
|
||||
|
||||
function _split(tags) {
|
||||
var _tags = tags.split(",")
|
||||
var tagsArr = []
|
||||
for (var i = 0; i < _tags.length; i++) {
|
||||
var tag = _tags[i].trim().toLowerCase()
|
||||
if (tag !== "") {
|
||||
if (tagsArr.indexOf(tag) === -1) {
|
||||
tagsArr.push(tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
return tagsArr
|
||||
}
|
||||
|
||||
function fixTags(tags) {
|
||||
return _split(tags).join(", ")
|
||||
}
|
||||
|
||||
function enable() {
|
||||
busy = true
|
||||
settings.pocketToken = ""
|
||||
_requestToken(function() {
|
||||
console.log("request token: " + root.request_token)
|
||||
pageStack.push(Qt.resolvedUrl("PocketAuthWebViewPage.qml"))
|
||||
}, function(code) {
|
||||
console.log("X-Error:" + xhr.getResponseHeader("X-Error"))
|
||||
console.log("Error while requesting Pocket token, X-Error-Code:" + code)
|
||||
notification.show(qsTr("Pocket authorization has failed."))
|
||||
busy = false
|
||||
})
|
||||
}
|
||||
|
||||
function check() {
|
||||
_accessToken(function() {
|
||||
console.log("access token: " + root.access_token)
|
||||
settings.pocketToken = root.access_token
|
||||
settings.pocketEnabled = true
|
||||
notification.show(qsTr("Pocket authorization was successful."))
|
||||
busy = false
|
||||
}, function(code) {
|
||||
console.log("X-Error:" + xhr.getResponseHeader("X-Error"))
|
||||
console.log("Error while requesting Pocket access token, X-Error-Code:" + code)
|
||||
notification.show(qsTr("Pocket authorization has failed."))
|
||||
busy = false
|
||||
})
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
busy = false
|
||||
}
|
||||
|
||||
function getAuthUrl() {
|
||||
return authorize_url + "?request_token=" + request_token + "&redirect_uri=" + redirect_uri
|
||||
}
|
||||
|
||||
function add(url, title) {
|
||||
if (settings.pocketQuickAdd)
|
||||
quickAdd(url, title, settings.pocketTags)
|
||||
else
|
||||
pageStack.push(Qt.resolvedUrl("PocketDialog.qml"),{"url": url, "title": title})
|
||||
}
|
||||
|
||||
function quickAdd(url, title, tags) {
|
||||
tags = tags == null ? "" : fixTags(tags)
|
||||
title = title == null ? "" : title.trim()
|
||||
var req = {
|
||||
consumer_key: consumer_key,
|
||||
access_token: access_token,
|
||||
url: url,
|
||||
title: title,
|
||||
tags: tags
|
||||
}
|
||||
var xhr = new XMLHttpRequest()
|
||||
xhr.open("POST", add_url)
|
||||
xhr.setRequestHeader("Content-Type", "application/json; charset=UTF8")
|
||||
xhr.setRequestHeader("X-Accept", "application/json")
|
||||
xhr.onreadystatechange = function () {
|
||||
if(xhr.readyState === XMLHttpRequest.DONE) {
|
||||
busy = false
|
||||
if (xhr.status === 200) {
|
||||
notification.show(qsTr("Article has been successfully added to Pocket."))
|
||||
settings.pocketTagsHistory = _split(settings.pocketTagsHistory + "," + tags).sort().join(",")
|
||||
} else {
|
||||
console.log("X-Error:" + xhr.getResponseHeader("X-Error"))
|
||||
var code = xhr.getResponseHeader("X-Error-Code")
|
||||
console.log("Error while adding article to Pocket, X-Error-Code:" + code)
|
||||
notification.show(qsTr("Error while adding article to Pocket."))
|
||||
}
|
||||
}
|
||||
}
|
||||
busy = true
|
||||
xhr.send(JSON.stringify(req))
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
function _requestToken(ok, error) {
|
||||
var req = {
|
||||
consumer_key: consumer_key,
|
||||
redirect_uri: redirect_uri
|
||||
}
|
||||
var xhr = new XMLHttpRequest()
|
||||
xhr.open("POST", request_url)
|
||||
xhr.setRequestHeader("Content-Type", "application/json; charset=UTF8")
|
||||
xhr.setRequestHeader("X-Accept", "application/json")
|
||||
xhr.onreadystatechange = function () {
|
||||
/*console.log("xhr.onreadystatechange")
|
||||
console.log(" xhr.readyState: " + xhr.readyState)
|
||||
console.log(" xhr.status: " + xhr.status)
|
||||
console.log(" xhr.responseType: " + xhr.responseType)
|
||||
console.log(" xhr.responseURL : " + xhr.responseURL )
|
||||
console.log(" xhr.statusText: " + xhr.statusText)
|
||||
console.log(" xhr.responseText: " + xhr.responseText)
|
||||
console.log(" X-Error-Code:" + xhr.getResponseHeader("X-Error-Code"))*/
|
||||
if(xhr.readyState === XMLHttpRequest.DONE) {
|
||||
if (xhr.status === 200) {
|
||||
var res = JSON.parse(xhr.responseText)
|
||||
root.request_token = res.code
|
||||
ok()
|
||||
} else {
|
||||
error(xhr.getResponseHeader("X-Error-Code"))
|
||||
}
|
||||
}
|
||||
}
|
||||
xhr.send(JSON.stringify(req))
|
||||
}
|
||||
|
||||
function _accessToken(ok, error) {
|
||||
var req = {
|
||||
consumer_key: consumer_key,
|
||||
code: request_token
|
||||
}
|
||||
var xhr = new XMLHttpRequest()
|
||||
xhr.open("POST", oauth_authorize_url)
|
||||
xhr.setRequestHeader("Content-Type", "application/json; charset=UTF8")
|
||||
xhr.setRequestHeader("X-Accept", "application/json")
|
||||
xhr.onreadystatechange = function () {
|
||||
/*console.log("xhr.onreadystatechange")
|
||||
console.log(" xhr.readyState: " + xhr.readyState)
|
||||
console.log(" xhr.status: " + xhr.status)
|
||||
console.log(" xhr.responseType: " + xhr.responseType)
|
||||
console.log(" xhr.responseURL : " + xhr.responseURL )
|
||||
console.log(" xhr.statusText: " + xhr.statusText)
|
||||
console.log(" xhr.responseText: " + xhr.responseText)
|
||||
console.log(" X-Error-Code:" + xhr.getResponseHeader("X-Error-Code"))*/
|
||||
if(xhr.readyState === XMLHttpRequest.DONE) {
|
||||
if (xhr.status === 200) {
|
||||
var res = JSON.parse(xhr.responseText)
|
||||
root.access_token = res.access_token
|
||||
ok()
|
||||
} else {
|
||||
error(xhr.getResponseHeader("X-Error-Code"))
|
||||
}
|
||||
}
|
||||
}
|
||||
xhr.send(JSON.stringify(req))
|
||||
}
|
||||
}
|
||||
153
sailfish/qml/PocketAuthWebViewPage.qml
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
Copyright (C) 2017 Michal Kosciesza <michal@mkiol.net>
|
||||
|
||||
This file is part of Kaktus.
|
||||
|
||||
Kaktus 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.
|
||||
|
||||
Kaktus 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 Kaktus. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick 2.1
|
||||
import Sailfish.Silica 1.0
|
||||
import QtWebKit 3.0
|
||||
|
||||
Page {
|
||||
id: root
|
||||
|
||||
property bool showBar: false
|
||||
property bool doPop: false
|
||||
property bool done: false
|
||||
|
||||
function init() {
|
||||
view.url = pocket.getAuthUrl()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
init()
|
||||
}
|
||||
|
||||
onForwardNavigationChanged: {
|
||||
if (forwardNavigation)
|
||||
forwardNavigation = false
|
||||
}
|
||||
|
||||
showNavigationIndicator: false
|
||||
|
||||
allowedOrientations: {
|
||||
switch (settings.allowedOrientations) {
|
||||
case 1:
|
||||
return Orientation.Portrait;
|
||||
case 2:
|
||||
return Orientation.Landscape;
|
||||
}
|
||||
return Orientation.Landscape | Orientation.Portrait;
|
||||
}
|
||||
|
||||
onStatusChanged: {
|
||||
if (status === PageStatus.Active && doPop) {
|
||||
pageStack.pop()
|
||||
}
|
||||
}
|
||||
|
||||
SilicaWebView {
|
||||
id: view
|
||||
|
||||
anchors {left: parent.left; right: parent.right}
|
||||
height: controlbar.open ? parent.height - controlbar.height : parent.height
|
||||
clip: true
|
||||
|
||||
_cookiesEnabled: true
|
||||
experimental.preferences.offlineWebApplicationCacheEnabled: false
|
||||
experimental.preferences.localStorageEnabled: true
|
||||
//experimental.preferences.privateBrowsingEnabled: true
|
||||
|
||||
onLoadingChanged: {
|
||||
switch (loadRequest.status) {
|
||||
case WebView.LoadStartedStatus:
|
||||
proggressPanel.text = qsTr("Loading page content...");
|
||||
proggressPanel.open = true
|
||||
break;
|
||||
case WebView.LoadSucceededStatus:
|
||||
proggressPanel.open = false
|
||||
break;
|
||||
case WebView.LoadFailedStatus:
|
||||
proggressPanel.open = false
|
||||
break;
|
||||
default:
|
||||
proggressPanel.open = false
|
||||
}
|
||||
}
|
||||
|
||||
onNavigationRequested: {
|
||||
if (!Qt.application.active) {
|
||||
request.action = WebView.IgnoreRequest
|
||||
}
|
||||
}
|
||||
|
||||
onUrlChanged: {
|
||||
console.log("Url changed:", url)
|
||||
|
||||
var surl = url.toString()
|
||||
if (surl === "https://getpocket.com/a/") {
|
||||
init()
|
||||
return
|
||||
}
|
||||
|
||||
if (surl === "kaktus:authorizationFinished") {
|
||||
done = true
|
||||
pocket.check()
|
||||
if (status === PageStatus.Active) {
|
||||
pageStack.pop()
|
||||
} else {
|
||||
doPop = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IconBar {
|
||||
id: controlbar
|
||||
flickable: view
|
||||
theme: "black"
|
||||
IconBarItem {
|
||||
text: qsTr("Back")
|
||||
icon: "image://theme/icon-m-back"
|
||||
onClicked: view.canGoBack ? view.goBack() : pageStack.pop()
|
||||
}
|
||||
}
|
||||
|
||||
ProgressPanel {
|
||||
id: proggressPanel
|
||||
transparent: false
|
||||
anchors.left: parent.left
|
||||
anchors.bottom: parent.bottom
|
||||
cancelable: true
|
||||
onCloseClicked: view.stop()
|
||||
}
|
||||
|
||||
// Workaround for 'High Power Consumption' webkit bug
|
||||
Connections {
|
||||
target: Qt.application
|
||||
onActiveChanged: {
|
||||
if(!Qt.application.active && settings.powerSaveMode) {
|
||||
pageStack.pop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
if (!done) {
|
||||
pocket.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
162
sailfish/qml/PocketDialog.qml
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
Copyright (C) 2017 Michal Kosciesza <michal@mkiol.net>
|
||||
|
||||
This file is part of Kaktus.
|
||||
|
||||
Kaktus 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.
|
||||
|
||||
Kaktus 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 Kaktus. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick 2.0
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
|
||||
Dialog {
|
||||
id: root
|
||||
|
||||
property bool showBar: false
|
||||
|
||||
property string url
|
||||
property string title
|
||||
|
||||
property var selectedTags: []
|
||||
|
||||
function checkTags() {
|
||||
selectedTags = pocket._split(tags.text)
|
||||
tags.text = selectedTags.join(", ")
|
||||
tags.cursorPosition = tags.text.length
|
||||
}
|
||||
|
||||
function addTag(tag) {
|
||||
tags.text += tags.text === "" ? tag : ", " + tag
|
||||
checkTags()
|
||||
}
|
||||
|
||||
allowedOrientations: {
|
||||
switch (settings.allowedOrientations) {
|
||||
case 1:
|
||||
return Orientation.Portrait;
|
||||
case 2:
|
||||
return Orientation.Landscape;
|
||||
}
|
||||
return Orientation.Landscape | Orientation.Portrait;
|
||||
}
|
||||
|
||||
onAccepted: {
|
||||
pocket.quickAdd(url, title, tags.text)
|
||||
}
|
||||
|
||||
SilicaFlickable {
|
||||
id: flick
|
||||
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
}
|
||||
|
||||
height: app.flickHeight
|
||||
|
||||
contentHeight: content.height
|
||||
|
||||
Column {
|
||||
id: content
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
spacing: Theme.paddingMedium
|
||||
|
||||
DialogHeader {
|
||||
acceptText : qsTr("Add to Pocket")
|
||||
}
|
||||
|
||||
TextArea {
|
||||
id: tags
|
||||
anchors.left: parent.left; anchors.right: parent.right
|
||||
inputMethodHints: Qt.ImhNoAutoUppercase
|
||||
wrapMode: TextEdit.WordWrap
|
||||
placeholderText: qsTr("Insert comma seperated tags")
|
||||
label: qsTr("Tags")
|
||||
text: settings.pocketTags
|
||||
|
||||
EnterKey.iconSource: "image://theme/icon-m-enter-close"
|
||||
EnterKey.onClicked: parent.focus = true
|
||||
|
||||
onTextChanged: timer.restart()
|
||||
onFocusChanged: {
|
||||
if (!focus) {
|
||||
timer.stop()
|
||||
root.checkTags()
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
interval: 1000
|
||||
onTriggered: {
|
||||
timer.stop()
|
||||
root.checkTags()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SectionHeader {
|
||||
text: qsTr("Previously used tags")
|
||||
visible: settings.pocketTagsHistory !== ""
|
||||
}
|
||||
|
||||
Flow {
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
leftMargin: Theme.horizontalPageMargin
|
||||
rightMargin: Theme.horizontalPageMargin
|
||||
}
|
||||
spacing: Theme.paddingMedium
|
||||
|
||||
Repeater {
|
||||
model: settings.pocketTagsHistory.split(",")
|
||||
|
||||
Item {
|
||||
width: tagLabel.width + 2 * Theme.paddingSmall
|
||||
height: tagLabel.height
|
||||
enabled: root.selectedTags.indexOf(modelData) ===-1
|
||||
|
||||
Label {
|
||||
id: tagLabel
|
||||
anchors.centerIn: parent
|
||||
text: modelData
|
||||
color: parent.enabled ?
|
||||
mouse.pressed ? Theme.highlightColor : Theme.primaryColor :
|
||||
Theme.secondaryColor
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouse
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
root.addTag(modelData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VerticalScrollDecorator {
|
||||
flickable: flick
|
||||
}
|
||||
}
|
||||
|
|
@ -37,25 +37,41 @@ Page {
|
|||
|
||||
ActiveDetector {}
|
||||
|
||||
SilicaListView {
|
||||
anchors { top: parent.top; left: parent.left; right: parent.right }
|
||||
clip: true
|
||||
|
||||
height: app.flickHeight
|
||||
|
||||
header: PageHeader {
|
||||
title: qsTr("Settings")
|
||||
SilicaFlickable {
|
||||
id: flick
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
}
|
||||
height: app.flickHeight
|
||||
clip: true
|
||||
contentHeight: content.height
|
||||
|
||||
Column {
|
||||
id: content
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
spacing: Theme.paddingMedium
|
||||
|
||||
PageHeader {
|
||||
title: qsTr("Settings")
|
||||
}
|
||||
|
||||
model: VisualItemModel {
|
||||
id: model
|
||||
Item {
|
||||
anchors { left: parent.left; right: parent.right}
|
||||
height: Math.max(icon.height, label.height)
|
||||
|
||||
Image {
|
||||
id: icon
|
||||
anchors { right: label.left; rightMargin: Theme.paddingMedium }
|
||||
anchors {
|
||||
right: label.left
|
||||
rightMargin: Theme.paddingMedium
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
source: app.isNetvibes ? "nv.png" :
|
||||
app.isOldReader ? "oldreader.png" : "feedly.png"
|
||||
width: Theme.iconSizeMedium
|
||||
|
|
@ -65,14 +81,17 @@ Page {
|
|||
|
||||
Label {
|
||||
id: label
|
||||
anchors { right: parent.right; rightMargin: Theme.paddingLarge}
|
||||
anchors {
|
||||
right: parent.right
|
||||
rightMargin: Theme.paddingLarge
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
text: app.isNetvibes ? "Netvibes":
|
||||
app.isOldReader ? "Old Reader" : "Feedly"
|
||||
wrapMode: Text.WordWrap
|
||||
horizontalAlignment: Text.AlignRight
|
||||
color: Theme.highlightColor
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
y: Theme.paddingSmall/2
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -420,6 +439,15 @@ Page {
|
|||
}
|
||||
}
|
||||
|
||||
ButtonItem {
|
||||
button.text: qsTr("Delete cookies")
|
||||
description: qsTr("Clear web viewer cache and cookies. Changes will take effect after restart.")
|
||||
button.onClicked: {
|
||||
utils.resetQtWebKit()
|
||||
notification.show(qsTr("Cache and cookies have been deleted."))
|
||||
}
|
||||
}
|
||||
|
||||
SectionHeader {
|
||||
text: qsTr("UI")
|
||||
}
|
||||
|
|
@ -692,22 +720,6 @@ Page {
|
|||
settings.showOldestFirst = true; break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
width: root.width
|
||||
label: qsTr("Context menu style")
|
||||
currentIndex: settings.iconContextMenu ? 0 : 1
|
||||
|
||||
menu: ContextMenu {
|
||||
MenuItem { text: qsTr("Icons") }
|
||||
MenuItem { text: qsTr("Text") }
|
||||
}
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
settings.iconContextMenu = (currentIndex == 0 ? true : false);
|
||||
}
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
|
|
@ -760,7 +772,17 @@ Page {
|
|||
qsTr("List of articles can be filtered to display all articles, unread and starred or only unread.")
|
||||
}
|
||||
|
||||
|
||||
TextSwitch {
|
||||
text: qsTr("Social features")
|
||||
enabled: app.isOldReader
|
||||
description: qsTr("Following Old Reader's social features will be enabled: Following folder, Sharing article with followers, Like option, Liked articles view mode.")
|
||||
onCheckedChanged: {
|
||||
settings.showBroadcast = checked;
|
||||
}
|
||||
Component.onCompleted: {
|
||||
checked = settings.showBroadcast;
|
||||
}
|
||||
}
|
||||
|
||||
TextSwitch {
|
||||
text: qsTr("Expanded items")
|
||||
|
|
@ -786,18 +808,6 @@ Page {
|
|||
}
|
||||
}
|
||||
|
||||
TextSwitch {
|
||||
text: qsTr("Social features")
|
||||
enabled: app.isOldReader
|
||||
description: qsTr("Following Old Reader's social features will be enabled: Following folder, Sharing article with followers, Like option, Liked articles view mode.")
|
||||
onCheckedChanged: {
|
||||
settings.showBroadcast = checked;
|
||||
}
|
||||
Component.onCompleted: {
|
||||
checked = settings.showBroadcast;
|
||||
}
|
||||
}
|
||||
|
||||
TextSwitch {
|
||||
text: qsTr("Power save mode")
|
||||
description: qsTr("When the phone or app goes to the idle state, "+
|
||||
|
|
@ -824,10 +834,127 @@ Page {
|
|||
onCurrentIndexChanged: settings.allowedOrientations = currentIndex
|
||||
}
|
||||
|
||||
SectionHeader {
|
||||
text: qsTr("Pocket")
|
||||
}
|
||||
|
||||
TextSwitchWithIcon {
|
||||
text: qsTr("Pocket integration")
|
||||
description: qsTr("Pocket is an Internet tool for saving articles to read later. Integration implemented in Kaktus provides \"Add to Pocket\" button in the articles list and in the web viewer.")
|
||||
iconSource: "image://icons/icon-m-pocket"
|
||||
checked: settings.pocketEnabled
|
||||
busy: pocket.busy
|
||||
enabled: dm.online
|
||||
automaticCheck: false
|
||||
onClicked: {
|
||||
if (checked) {
|
||||
settings.pocketEnabled = false
|
||||
} else {
|
||||
pocket.enable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextFieldItem {
|
||||
id: pocketTagsField
|
||||
enabled: settings.pocketEnabled
|
||||
textField.placeholderText: qsTr("Default tags")
|
||||
textField.label: qsTr("Default tags")
|
||||
textField.labelVisible: false
|
||||
description: qsTr("List of comma seperated tags that will be automatically inserted when you add article to Pocket.")
|
||||
textField.inputMethodHints: Qt.ImhNoAutoUppercase
|
||||
textField.onTextChanged: timer.restart()
|
||||
Component.onCompleted: textField.text = settings.pocketTags
|
||||
|
||||
EnterKey.iconSource: "image://theme/icon-m-enter-close"
|
||||
EnterKey.onClicked: {
|
||||
checkTags()
|
||||
parent.focus = true
|
||||
}
|
||||
|
||||
onFocusChanged: {
|
||||
if (!focus)
|
||||
checkTags()
|
||||
}
|
||||
|
||||
function checkTags() {
|
||||
timer.stop()
|
||||
pocketTagsField.textField.text = pocket.fixTags(pocketTagsField.textField.text)
|
||||
settings.pocketTags = pocketTagsField.textField.text
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
interval: 1000
|
||||
onTriggered: pocketTagsField.checkTags()
|
||||
}
|
||||
}
|
||||
|
||||
TextSwitch {
|
||||
text: qsTr("Quick adding")
|
||||
description: qsTr("If enabled, article will be send to Pocket immediately after you click on \"Add to Pocket\" button, so without any confirmation dialog. All tags from \"Default tags\" field will be automatically added.")
|
||||
checked: settings.pocketQuickAdd
|
||||
enabled: settings.pocketEnabled
|
||||
onCheckedChanged: {
|
||||
settings.pocketQuickAdd = checked
|
||||
}
|
||||
}
|
||||
|
||||
ButtonItem {
|
||||
enabled: settings.pocketEnabled
|
||||
button.text: qsTr("Delete saved tags")
|
||||
button.onClicked: {
|
||||
settings.pocketTagsHistory = ""
|
||||
notification.show(qsTr("Saved tags have been deleted."))
|
||||
}
|
||||
}
|
||||
|
||||
/*SectionHeader {
|
||||
text: qsTr("Experimental")
|
||||
}
|
||||
|
||||
Column {
|
||||
x: Theme.horizontalPageMargin
|
||||
spacing: Theme.paddingMedium
|
||||
|
||||
Row {
|
||||
spacing: Theme.paddingMedium
|
||||
|
||||
Image {
|
||||
source: "image://icons/icon-m-good"
|
||||
height: Theme.iconSizeSmall
|
||||
width: Theme.iconSizeSmall
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Label {
|
||||
text: ai.evaluationCount(1)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: Theme.paddingMedium
|
||||
|
||||
Image {
|
||||
source: "image://icons/icon-m-bad"
|
||||
height: Theme.iconSizeSmall
|
||||
width: Theme.iconSizeSmall
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Label {
|
||||
text: ai.evaluationCount(-1)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
/*SectionHeader {
|
||||
text: qsTr("Other")
|
||||
}
|
||||
|
||||
|
||||
Button {
|
||||
text: qsTr("Show User Guide")
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
|
@ -836,13 +963,12 @@ Page {
|
|||
}
|
||||
}*/
|
||||
|
||||
Item {
|
||||
height: Theme.paddingMedium
|
||||
width: height
|
||||
}
|
||||
|
||||
Spacer {}
|
||||
}
|
||||
}
|
||||
|
||||
VerticalScrollDecorator {}
|
||||
VerticalScrollDecorator {
|
||||
flickable: flick
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
55
sailfish/qml/ShareLinkPage.qml
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
Copyright (C) 2017 Michal Kosciesza <michal@mkiol.net>
|
||||
|
||||
This file is part of Kaktus.
|
||||
|
||||
Kaktus 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.
|
||||
|
||||
Kaktus 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 Kaktus. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick 2.0
|
||||
import Sailfish.Silica 1.0
|
||||
import Sailfish.TransferEngine 1.0
|
||||
|
||||
Page {
|
||||
id: root
|
||||
|
||||
property string link
|
||||
property string linkTitle
|
||||
|
||||
readonly property bool showBar: false
|
||||
|
||||
ShareMethodList {
|
||||
id: shareMethodList
|
||||
|
||||
anchors { top: parent.top; left: parent.left; right: parent.right }
|
||||
clip: true
|
||||
height: app.flickHeight
|
||||
|
||||
header: PageHeader {
|
||||
title: qsTr("Share link")
|
||||
}
|
||||
|
||||
filter: "text/x-url"
|
||||
content: {
|
||||
"type": "text/x-url",
|
||||
"status": root.link,
|
||||
"linkTitle": root.linkTitle
|
||||
}
|
||||
|
||||
ViewPlaceholder {
|
||||
enabled: shareMethodList.model.count === 0
|
||||
text: qsTr("No sharing accounts available. You can add accounts in settings")
|
||||
}
|
||||
}
|
||||
}
|
||||
34
sailfish/qml/SmallIconButton.qml
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
Copyright (C) 2017 Michal Kosciesza <michal@mkiol.net>
|
||||
|
||||
This file is part of Kaktus.
|
||||
|
||||
Kaktus 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.
|
||||
|
||||
Kaktus 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 Kaktus. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick 2.0
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
IconButton {
|
||||
id: root
|
||||
|
||||
property int iconSize: Theme.iconSizeSmall
|
||||
property int marginV: Theme.paddingMedium
|
||||
property int marginH: Theme.paddingMedium
|
||||
|
||||
icon.height: iconSize
|
||||
icon.width: iconSize
|
||||
width: Theme.iconSizeSmall + marginH
|
||||
height: Theme.iconSizeSmall + marginV
|
||||
}
|
||||
26
sailfish/qml/Spacer.qml
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
Copyright (C) 2017 Michal Kosciesza <michal@mkiol.net>
|
||||
|
||||
This file is part of Kaktus.
|
||||
|
||||
Kaktus 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.
|
||||
|
||||
Kaktus 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 Kaktus. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick 2.0
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
Item {
|
||||
width: Theme.itemSizeSmall
|
||||
height: Theme.itemSizeSmall
|
||||
}
|
||||
58
sailfish/qml/TextAreaItem.qml
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
Copyright (C) 2017 Michal Kosciesza <michal@mkiol.net>
|
||||
|
||||
This file is part of Kaktus.
|
||||
|
||||
Kaktus 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.
|
||||
|
||||
Kaktus 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 Kaktus. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick 2.0
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property alias textArea: _textArea
|
||||
property alias description: desc.text
|
||||
|
||||
opacity: enabled ? 1.0 : 0.4
|
||||
height: col.height + 4*Theme.paddingSmall
|
||||
|
||||
anchors {left: parent.left; right: parent.right}
|
||||
|
||||
Column {
|
||||
id: col
|
||||
anchors {
|
||||
top: parent.top; topMargin: 2*Theme.paddingSmall
|
||||
left: parent.left; right: parent.right
|
||||
}
|
||||
|
||||
TextArea {
|
||||
id: _textArea
|
||||
enabled: root.enabled
|
||||
anchors {left: parent.left; right: parent.right}
|
||||
}
|
||||
|
||||
Label {
|
||||
id: desc
|
||||
anchors {
|
||||
left: parent.left; right: parent.right;
|
||||
leftMargin: Theme.horizontalPageMargin; rightMargin: Theme.horizontalPageMargin
|
||||
}
|
||||
wrapMode: Text.Wrap
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
color: Theme.secondaryColor
|
||||
}
|
||||
}
|
||||
}
|
||||
58
sailfish/qml/TextFieldItem.qml
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
Copyright (C) 2017 Michal Kosciesza <michal@mkiol.net>
|
||||
|
||||
This file is part of Kaktus.
|
||||
|
||||
Kaktus 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.
|
||||
|
||||
Kaktus 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 Kaktus. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick 2.0
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property alias textField: _textField
|
||||
property alias description: desc.text
|
||||
|
||||
opacity: enabled ? 1.0 : 0.4
|
||||
height: col.height + 4*Theme.paddingSmall
|
||||
|
||||
anchors {left: parent.left; right: parent.right}
|
||||
|
||||
Column {
|
||||
id: col
|
||||
anchors {
|
||||
top: parent.top; topMargin: 2*Theme.paddingSmall
|
||||
left: parent.left; right: parent.right
|
||||
}
|
||||
|
||||
TextField{
|
||||
id: _textField
|
||||
enabled: root.enabled
|
||||
anchors {left: parent.left; right: parent.right}
|
||||
}
|
||||
|
||||
Label {
|
||||
id: desc
|
||||
anchors {
|
||||
left: parent.left; right: parent.right;
|
||||
leftMargin: Theme.horizontalPageMargin; rightMargin: Theme.horizontalPageMargin
|
||||
}
|
||||
wrapMode: Text.Wrap
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
color: Theme.secondaryColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -27,6 +27,9 @@ Item {
|
|||
property alias description: textswitch.description
|
||||
property alias checked: textswitch.checked
|
||||
property alias iconSource: icon.source
|
||||
property alias automaticCheck: textswitch.automaticCheck
|
||||
property alias busy: textswitch.busy
|
||||
signal clicked
|
||||
|
||||
anchors.left: parent.left; anchors.right: parent.right
|
||||
height: Math.max(textswitch.height, icon.height)
|
||||
|
|
@ -35,6 +38,7 @@ Item {
|
|||
id: textswitch
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width - icon.width - icon.anchors.rightMargin
|
||||
onClicked: root.clicked()
|
||||
}
|
||||
|
||||
Image {
|
||||
|
|
|
|||
|
|
@ -50,6 +50,10 @@ Page {
|
|||
property bool nightModePossible: true
|
||||
property bool autoReaderMode: settings.readerMode
|
||||
|
||||
function share() {
|
||||
pageStack.push(Qt.resolvedUrl("ShareLinkPage.qml"),{"link": root.onlineUrl, "linkTitle": root.title});
|
||||
}
|
||||
|
||||
function openUrlEntryInBrowser(url) {
|
||||
notification.show(qsTr("Launching an external browser..."))
|
||||
Qt.openUrlExternally(url)
|
||||
|
|
@ -138,6 +142,7 @@ Page {
|
|||
}
|
||||
|
||||
function messageReceivedHandler(message) {
|
||||
//console.log("view.url: " + view.url)
|
||||
if (message.type === "inited") {
|
||||
// NightMode
|
||||
root.nightModePossible = true
|
||||
|
|
@ -179,8 +184,6 @@ Page {
|
|||
view.experimental.postMessage(JSON.stringify({ "type": message, "data": data }));
|
||||
}
|
||||
|
||||
ActiveDetector {}
|
||||
|
||||
showNavigationIndicator: false
|
||||
|
||||
allowedOrientations: {
|
||||
|
|
@ -199,8 +202,15 @@ Page {
|
|||
Connections {
|
||||
target: Qt.application
|
||||
onActiveChanged: {
|
||||
if(!Qt.application.active && settings.powerSaveMode) {
|
||||
pageStack.pop();
|
||||
if(!Qt.application.active) {
|
||||
if (settings.powerSaveMode && root.status === PageStatus.Active) {
|
||||
pageStack.pop()
|
||||
return
|
||||
}
|
||||
if (root.status !== PageStatus.Active) {
|
||||
pageStack.pop(pageStack.previousPage(root), PageStackAction.Immediate)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -403,6 +413,23 @@ Page {
|
|||
}
|
||||
}
|
||||
|
||||
IconMenuItem {
|
||||
text: qsTr("Add to Pocket")
|
||||
visible: settings.pocketEnabled
|
||||
enabled: settings.pocketEnabled && dm.online
|
||||
icon.source: "image://icons/icon-m-pocket"
|
||||
busy: pocket.busy
|
||||
onClicked: {
|
||||
pocket.add(root.onlineUrl, root.title)
|
||||
}
|
||||
}
|
||||
|
||||
IconMenuItem {
|
||||
text: qsTr("Share link")
|
||||
icon.source: "image://theme/icon-m-share"
|
||||
onClicked: root.share()
|
||||
}
|
||||
|
||||
IconBarItem {
|
||||
text: qsTr("Toggle Like")
|
||||
icon: root.liked ? "image://icons/icon-m-like-selected" : "image://icons/icon-m-like"
|
||||
|
|
|
|||
|
|
@ -451,5 +451,8 @@ ApplicationWindow {
|
|||
x: app.orientation==Orientation.Portrait ? 0 : app.width
|
||||
}
|
||||
|
||||
Pocket {
|
||||
id: pocket
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,14 @@
|
|||
# * date Author's Name <author's email> version-release
|
||||
# - Summary of changes
|
||||
|
||||
* Wed Mar 15 2017 Michal Kosciesza 2.6.0-2
|
||||
- Pocket integration
|
||||
- New app icon
|
||||
|
||||
* Sun Feb 19 2017 Michal Kosciesza 2.6.0-1
|
||||
- Share link option (only in openrepos package)
|
||||
- ES translation update
|
||||
|
||||
* Tue Feb 09 2017 Michal Kosciesza 2.5.3-1
|
||||
- FIX: Sync process did not download all saved articles in Netvibes
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ Name: harbour-kaktus
|
|||
%{!?qtc_make:%define qtc_make make}
|
||||
%{?qtc_builddir:%define _builddir %qtc_builddir}
|
||||
Summary: Kaktus
|
||||
Version: 2.5.3
|
||||
Release: 1
|
||||
Version: 2.6.0
|
||||
Release: 2
|
||||
Group: Qt/Qt
|
||||
License: LICENSE
|
||||
URL: https://github.com/mkiol/kaktus
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
Name: harbour-kaktus
|
||||
Summary: Kaktus
|
||||
Version: 2.5.3
|
||||
Release: 1
|
||||
Version: 2.6.0
|
||||
Release: 2
|
||||
# The contents of the Group field should be one of the groups listed here:
|
||||
# http://gitorious.org/meego-developer-tools/spectacle/blobs/master/data/GROUPS
|
||||
Group: Qt/Qt
|
||||
|
|
|
|||
180
sailfish/src/ai.cpp
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
Copyright (C) 2017 Michal Kosciesza <michal@mkiol.net>
|
||||
|
||||
This file is part of Kaktus.
|
||||
|
||||
Kaktus 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.
|
||||
|
||||
Kaktus 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 Kaktus. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QSqlQuery>
|
||||
|
||||
#include "ai.h"
|
||||
#include "settings.h"
|
||||
|
||||
Ai::Ai(QObject *parent) : QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
Ai::~Ai()
|
||||
{
|
||||
db.close();
|
||||
QSqlDatabase::removeDatabase("qt_sql_kaktusai_connection");
|
||||
}
|
||||
|
||||
void Ai::init()
|
||||
{
|
||||
if (!openDB()) {
|
||||
qWarning() << "Error when trying to open AI-DB.";
|
||||
return;
|
||||
}
|
||||
|
||||
int ver = version();
|
||||
if (Ai::VERSION != ver) {
|
||||
if (ver != 0) {
|
||||
qWarning() << "AI-DB version mismatch. Exising version is"
|
||||
<< ver << ", but required is" << Ai::VERSION;
|
||||
deleteDB();
|
||||
|
||||
init();
|
||||
if (!openDB()) {
|
||||
qWarning() << "Error when trying to open AI-DB.";
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
qWarning() << "AI-DB file doesn't exist.";
|
||||
}
|
||||
|
||||
createDB();
|
||||
}
|
||||
}
|
||||
|
||||
bool Ai::openDB()
|
||||
{
|
||||
Settings *s = Settings::instance();
|
||||
|
||||
db = QSqlDatabase::addDatabase("QSQLITE","qt_sql_kaktusai_connection");
|
||||
dbFilePath = s->getSettingsDir();
|
||||
dbFilePath.append(QDir::separator()).append("ai.db");
|
||||
dbFilePath = QDir::toNativeSeparators(dbFilePath);
|
||||
db.setDatabaseName(dbFilePath);
|
||||
|
||||
return db.open();
|
||||
}
|
||||
|
||||
void Ai::deleteDB()
|
||||
{
|
||||
db.close();
|
||||
QSqlDatabase::removeDatabase("qt_sql_kaktusai_connection");
|
||||
|
||||
if (!QFile::exists(dbFilePath)) {
|
||||
qWarning() << "AI-DB file doesn't exist.";
|
||||
return;
|
||||
}
|
||||
|
||||
QFile::remove(dbFilePath);
|
||||
}
|
||||
|
||||
int Ai::version()
|
||||
{
|
||||
QSqlQuery query(db);
|
||||
query.exec("PRAGMA user_version");
|
||||
query.first();
|
||||
return query.value(0).toInt();
|
||||
}
|
||||
|
||||
void Ai::createDB()
|
||||
{
|
||||
QSqlQuery query(db);
|
||||
query.exec("PRAGMA journal_mode = MEMORY");
|
||||
query.exec("PRAGMA synchronous = OFF");
|
||||
query.exec(QString("PRAGMA user_version = %1").arg(Ai::VERSION));
|
||||
|
||||
query.exec("CREATE TABLE IF NOT EXISTS entries ("
|
||||
"id VARCHAR(50) PRIMARY KEY, "
|
||||
"title TEXT, "
|
||||
"evaluation INTEGER DEFAULT 0, "
|
||||
"status INTEGER DEFAULT 1, "
|
||||
"last_update TIMESTAMP "
|
||||
");");
|
||||
query.exec("CREATE INDEX IF NOT EXISTS entries_evaluation "
|
||||
"ON entries(id, evaluation);");
|
||||
}
|
||||
|
||||
void Ai::addEvaluation(const QString &id, const QString &title, int evaluation)
|
||||
{
|
||||
if (!db.isOpen()) {
|
||||
qWarning() << "AI-DB is not open.";
|
||||
return;
|
||||
}
|
||||
|
||||
QSqlQuery query(db);
|
||||
query.prepare("INSERT OR REPLACE INTO entries (id, title, evaluation, last_update) VALUES (?,?,?,?)");
|
||||
query.addBindValue(id);
|
||||
query.addBindValue(title);
|
||||
query.addBindValue(evaluation);
|
||||
query.addBindValue(QDateTime::currentDateTimeUtc().toTime_t());
|
||||
|
||||
if (!query.exec())
|
||||
qWarning() << "SQL error:" << query.lastQuery();
|
||||
}
|
||||
|
||||
int Ai::evaluationCount(const int evaluation)
|
||||
{
|
||||
if (!db.isOpen()) {
|
||||
qWarning() << "AI-DB is not open.";
|
||||
return 0;
|
||||
}
|
||||
|
||||
QSqlQuery query(db);
|
||||
query.prepare("SELECT count(*) FROM entries WHERE evaluation = ?");
|
||||
query.addBindValue(evaluation);
|
||||
|
||||
if (!query.exec()) {
|
||||
qWarning() << "SQL error:" << query.lastQuery();
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (query.next()) {
|
||||
return query.value(0).toInt();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Ai::evaluation(const QString &id)
|
||||
{
|
||||
if (!db.isOpen()) {
|
||||
qWarning() << "AI-DB is not open.";
|
||||
return 0;
|
||||
}
|
||||
|
||||
QSqlQuery query(db);
|
||||
query.prepare("SELECT evaluation FROM entries WHERE id = ? LIMIT 1");
|
||||
query.addBindValue(id);
|
||||
|
||||
if (!query.exec()) {
|
||||
qWarning() << "SQL error:" << query.lastQuery();
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (query.next()) {
|
||||
return query.value(0).toInt();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
49
sailfish/src/ai.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
Copyright (C) 2017 Michal Kosciesza <michal@mkiol.net>
|
||||
|
||||
This file is part of Kaktus.
|
||||
|
||||
Kaktus 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.
|
||||
|
||||
Kaktus 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 Kaktus. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef AI_H
|
||||
#define AI_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSqlDatabase>
|
||||
#include <QString>
|
||||
|
||||
class Ai : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit Ai(QObject *parent = 0);
|
||||
~Ai();
|
||||
Q_INVOKABLE void init();
|
||||
Q_INVOKABLE void addEvaluation(const QString &id, const QString &title, int evaluation);
|
||||
Q_INVOKABLE int evaluation(const QString &id);
|
||||
Q_INVOKABLE int evaluationCount(const int evaluation);
|
||||
|
||||
private:
|
||||
const static int VERSION = 1;
|
||||
QSqlDatabase db;
|
||||
QString dbFilePath;
|
||||
|
||||
bool openDB();
|
||||
int version();
|
||||
void deleteDB();
|
||||
void createDB();
|
||||
};
|
||||
|
||||
#endif // AI_H
|
||||
|
|
@ -27,6 +27,12 @@ DatabaseManager::DatabaseManager(QObject *parent) :
|
|||
QObject(parent)
|
||||
{}
|
||||
|
||||
DatabaseManager::~DatabaseManager()
|
||||
{
|
||||
db.close();
|
||||
QSqlDatabase::removeDatabase("qt_sql_kaktus_connection");
|
||||
}
|
||||
|
||||
bool DatabaseManager::isSynced()
|
||||
{
|
||||
if (db.isOpen()) {
|
||||
|
|
|
|||
|
|
@ -173,6 +173,7 @@ public:
|
|||
};
|
||||
|
||||
explicit DatabaseManager(QObject *parent = 0);
|
||||
~DatabaseManager();
|
||||
|
||||
Q_INVOKABLE void init();
|
||||
Q_INVOKABLE void newInit();
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include <sailfishapp.h>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QStandardPaths>
|
||||
#endif
|
||||
#ifdef ANDROID
|
||||
#include <QQmlApplicationEngine>
|
||||
|
|
@ -43,14 +44,15 @@
|
|||
#include "utils.h"
|
||||
#include "settings.h"
|
||||
#include "networkaccessmanagerfactory.h"
|
||||
#include "ai.h"
|
||||
|
||||
static const char *APP_NAME = "Kaktus";
|
||||
static const char *AUTHOR = "Michal Kosciesza <michal@mkiol.net>";
|
||||
static const char *PAGE = "https://github.com/mkiol/kaktus";
|
||||
#ifdef KAKTUS_LIGHT
|
||||
static const char *VERSION = "2.5.3 (light edition)";
|
||||
static const char *VERSION = "2.6.0 (light edition)";
|
||||
#else
|
||||
static const char *VERSION = "2.5.3";
|
||||
static const char *VERSION = "2.6.0";
|
||||
#endif
|
||||
|
||||
|
||||
|
|
@ -94,24 +96,26 @@ int main(int argc, char *argv[])
|
|||
#ifdef SAILFISH
|
||||
//-- temp fix --
|
||||
// config file
|
||||
if (QFile::exists("/home/nemo/.config/harbour-kaktus/Kaktus.conf")) {
|
||||
QString path = QDir(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation)).path();
|
||||
if (QFile::exists(path + "/harbour-kaktus/Kaktus.conf")) {
|
||||
qWarning() << "Old config file exists -> doing migration";
|
||||
QFile::remove("/home/nemo/.config/harbour-kaktus/harbour-kaktus.conf");
|
||||
if (QFile::copy("/home/nemo/.config/harbour-kaktus/Kaktus.conf",
|
||||
"/home/nemo/.config/harbour-kaktus/harbour-kaktus.conf")) {
|
||||
QFile::remove("/home/nemo/.config/harbour-kaktus/Kaktus.conf");
|
||||
QFile::remove(path + "/harbour-kaktus/harbour-kaktus.conf");
|
||||
if (QFile::copy(path + "/harbour-kaktus/Kaktus.conf",
|
||||
path + "/harbour-kaktus/harbour-kaktus.conf")) {
|
||||
QFile::remove(path + "/harbour-kaktus/Kaktus.conf");
|
||||
}
|
||||
}
|
||||
// cache file
|
||||
QDir newDir("/home/nemo/.cache/harbour-kaktus/Kaktus/");
|
||||
path = QDir(QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation)).path();
|
||||
QDir newDir(path + "/harbour-kaktus/Kaktus/");
|
||||
if (newDir.exists()) {
|
||||
qWarning() << "Old cache dir exists -> doing migration";
|
||||
QDir oldDir("/home/nemo/.cache/harbour-kaktus/harbour-kaktus/");
|
||||
QDir oldDir(path + "/harbour-kaktus/harbour-kaktus/");
|
||||
if (oldDir.exists()) {
|
||||
oldDir.removeRecursively();
|
||||
}
|
||||
qDebug() << newDir.rename("/home/nemo/.cache/harbour-kaktus/Kaktus/",
|
||||
"/home/nemo/.cache/harbour-kaktus/harbour-kaktus/");
|
||||
qDebug() << newDir.rename(path + "/harbour-kaktus/Kaktus/",
|
||||
path + "/harbour-kaktus/harbour-kaktus/");
|
||||
}
|
||||
//--
|
||||
#endif
|
||||
|
|
@ -129,6 +133,7 @@ int main(int argc, char *argv[])
|
|||
DatabaseManager db; settings->db = &db;
|
||||
DownloadManager dm; settings->dm = &dm;
|
||||
CacheServer cache(&db); settings->cache = &cache;
|
||||
Ai ai; ai.init();
|
||||
|
||||
QObject::connect(engine.data(), SIGNAL(quit()), QCoreApplication::instance(), SLOT(quit()));
|
||||
|
||||
|
|
@ -139,6 +144,7 @@ int main(int argc, char *argv[])
|
|||
context->setContextProperty("utils", &utils);
|
||||
context->setContextProperty("dm", &dm);
|
||||
context->setContextProperty("cache", &cache);
|
||||
context->setContextProperty("ai", &ai);
|
||||
context->setContextProperty("settings", settings);
|
||||
|
||||
#ifdef SAILFISH
|
||||
|
|
|
|||
|
|
@ -62,6 +62,11 @@ Settings* Settings::instance()
|
|||
return Settings::inst;
|
||||
}
|
||||
|
||||
QString Settings::pocketConsumerKey()
|
||||
{
|
||||
return pocket_consumer_key;
|
||||
}
|
||||
|
||||
const QList<QVariant> Settings::viewModeHistory()
|
||||
{
|
||||
return settings.value("viewmodehistory").toList();
|
||||
|
|
@ -119,19 +124,6 @@ bool Settings::getShowOldestFirst()
|
|||
return settings.value("showoldestfirst", false).toBool();
|
||||
}
|
||||
|
||||
void Settings::setIconContextMenu(bool value)
|
||||
{
|
||||
if (getIconContextMenu() != value) {
|
||||
settings.setValue("iconcontextmenu", value);
|
||||
emit showOldestFirstChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool Settings::getIconContextMenu()
|
||||
{
|
||||
return settings.value("iconcontextmenu", true).toBool();
|
||||
}
|
||||
|
||||
void Settings::setShowBroadcast(bool value)
|
||||
{
|
||||
if (getShowBroadcast() != value) {
|
||||
|
|
@ -192,6 +184,96 @@ bool Settings::getReaderMode()
|
|||
return settings.value("readermode", false).toBool();
|
||||
}
|
||||
|
||||
void Settings::setPocketEnabled(bool value)
|
||||
{
|
||||
if (getPocketEnabled() != value) {
|
||||
settings.setValue("pocketenabled", value);
|
||||
emit pocketEnabledChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool Settings::getPocketEnabled()
|
||||
{
|
||||
return settings.value("pocketenabled", false).toBool();
|
||||
}
|
||||
|
||||
void Settings::setPocketQuickAdd(bool value)
|
||||
{
|
||||
if (getPocketQuickAdd() != value) {
|
||||
settings.setValue("pocketquickadd", value);
|
||||
emit pocketQuickAddChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool Settings::getPocketQuickAdd()
|
||||
{
|
||||
return settings.value("pocketquickadd", false).toBool();
|
||||
}
|
||||
|
||||
void Settings::setPocketFavorite(bool value)
|
||||
{
|
||||
if (getPocketFavorite() != value) {
|
||||
settings.setValue("pocketfavorite", value);
|
||||
emit pocketFavoriteChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool Settings::getPocketFavorite()
|
||||
{
|
||||
return settings.value("pocketfavorite", false).toBool();
|
||||
}
|
||||
|
||||
void Settings::setPocketToken(const QString &value)
|
||||
{
|
||||
if (getPocketToken() != value) {
|
||||
SimpleCrypt crypto(KEY);
|
||||
QString encryptedToken = crypto.encryptToString(value);
|
||||
if (!crypto.lastError() == SimpleCrypt::ErrorNoError) {
|
||||
emit error(532);
|
||||
return;
|
||||
}
|
||||
settings.setValue("pockettoken", encryptedToken);
|
||||
emit pocketTokenChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QString Settings::getPocketToken()
|
||||
{
|
||||
SimpleCrypt crypto(KEY);
|
||||
QString plainToken = crypto.decryptToString(settings.value("pockettoken", "").toString());
|
||||
if (!crypto.lastError() == SimpleCrypt::ErrorNoError) {
|
||||
emit error(531);
|
||||
return "";
|
||||
}
|
||||
return plainToken;
|
||||
}
|
||||
|
||||
void Settings::setPocketTags(const QString &value)
|
||||
{
|
||||
if (getPocketTags() != value) {
|
||||
settings.setValue("pockettags", value);
|
||||
emit pocketTagsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QString Settings::getPocketTags()
|
||||
{
|
||||
return settings.value("pockettags", "").toString();
|
||||
}
|
||||
|
||||
void Settings::setPocketTagsHistory(const QString &value)
|
||||
{
|
||||
if (getPocketTagsHistory() != value) {
|
||||
settings.setValue("pockettagshistory", value);
|
||||
emit pocketTagsHistoryChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QString Settings::getPocketTagsHistory()
|
||||
{
|
||||
return settings.value("pockettagshistory", "").toString();
|
||||
}
|
||||
|
||||
void Settings::setNightMode(bool value)
|
||||
{
|
||||
if (getNightMode() != value) {
|
||||
|
|
|
|||
|
|
@ -72,12 +72,17 @@ class Settings: public QObject
|
|||
Q_PROPERTY (bool showBroadcast READ getShowBroadcast WRITE setShowBroadcast NOTIFY showBroadcastChanged)
|
||||
Q_PROPERTY (bool showOldestFirst READ getShowOldestFirst WRITE setShowOldestFirst NOTIFY showOldestFirstChanged)
|
||||
Q_PROPERTY (bool syncRead READ getSyncRead WRITE setSyncRead NOTIFY syncReadChanged)
|
||||
Q_PROPERTY (bool iconContextMenu READ getIconContextMenu WRITE setIconContextMenu NOTIFY iconContextMenuChanged)
|
||||
Q_PROPERTY (bool doublePane READ getDoublePane WRITE setDoublePane NOTIFY doublePaneChanged)
|
||||
Q_PROPERTY (int clickBehavior READ getClickBehavior WRITE setClickBehavior NOTIFY clickBehaviorChanged)
|
||||
Q_PROPERTY (bool expandedMode READ getExpandedMode WRITE setExpandedMode NOTIFY expandedModeChanged)
|
||||
Q_PROPERTY (int webviewNavigation READ getWebviewNavigation WRITE setWebviewNavigation NOTIFY webviewNavigationChanged)
|
||||
Q_PROPERTY (bool nightMode READ getNightMode WRITE setNightMode NOTIFY nightModeChanged)
|
||||
Q_PROPERTY (bool pocketEnabled READ getPocketEnabled WRITE setPocketEnabled NOTIFY pocketEnabledChanged)
|
||||
Q_PROPERTY (bool pocketQuickAdd READ getPocketQuickAdd WRITE setPocketQuickAdd NOTIFY pocketQuickAddChanged)
|
||||
Q_PROPERTY (bool pocketFavorite READ getPocketFavorite WRITE setPocketFavorite NOTIFY pocketFavoriteChanged)
|
||||
Q_PROPERTY (QString pocketToken READ getPocketToken WRITE setPocketToken NOTIFY pocketTokenChanged)
|
||||
Q_PROPERTY (QString pocketTags READ getPocketTags WRITE setPocketTags NOTIFY pocketTagsChanged)
|
||||
Q_PROPERTY (QString pocketTagsHistory READ getPocketTagsHistory WRITE setPocketTagsHistory NOTIFY pocketTagsHistoryChanged)
|
||||
|
||||
public:
|
||||
static Settings* instance();
|
||||
|
|
@ -128,8 +133,23 @@ public:
|
|||
bool getShowBroadcast();
|
||||
void setShowBroadcast(bool value);
|
||||
|
||||
bool getIconContextMenu();
|
||||
void setIconContextMenu(bool value);
|
||||
bool getPocketEnabled();
|
||||
void setPocketEnabled(bool value);
|
||||
|
||||
bool getPocketQuickAdd();
|
||||
void setPocketQuickAdd(bool value);
|
||||
|
||||
bool getPocketFavorite();
|
||||
void setPocketFavorite(bool value);
|
||||
|
||||
void setPocketToken(const QString &value);
|
||||
QString getPocketToken();
|
||||
|
||||
void setPocketTags(const QString &value);
|
||||
QString getPocketTags();
|
||||
|
||||
void setPocketTagsHistory(const QString &value);
|
||||
QString getPocketTagsHistory();
|
||||
|
||||
void setDashboardInUse(const QString &value);
|
||||
QString getDashboardInUse();
|
||||
|
|
@ -254,6 +274,8 @@ public:
|
|||
|
||||
Q_INVOKABLE const QList<QVariant> viewModeHistory();
|
||||
|
||||
Q_INVOKABLE QString pocketConsumerKey();
|
||||
|
||||
signals:
|
||||
void offlineModeChanged();
|
||||
void autoOfflineChanged();
|
||||
|
|
@ -281,11 +303,16 @@ signals:
|
|||
void showBroadcastChanged();
|
||||
void showOldestFirstChanged();
|
||||
void syncReadChanged();
|
||||
void iconContextMenuChanged();
|
||||
void doublePaneChanged();
|
||||
void clickBehaviorChanged();
|
||||
void expandedModeChanged();
|
||||
void webviewNavigationChanged();
|
||||
void pocketEnabledChanged();
|
||||
void pocketTokenChanged();
|
||||
void pocketTagsChanged();
|
||||
void pocketTagsHistoryChanged();
|
||||
void pocketFavoriteChanged();
|
||||
void pocketQuickAddChanged();
|
||||
|
||||
/*
|
||||
501 - Unable create settings dir
|
||||
|
|
|
|||
|
|
@ -188,12 +188,18 @@ void Utils::copyToClipboard(const QString &text)
|
|||
void Utils::resetQtWebKit()
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
QString value = QDir(QStandardPaths::writableLocation(QStandardPaths::DataLocation)).path();
|
||||
QStringList dataDirs = QStandardPaths::standardLocations(QStandardPaths::DataLocation);
|
||||
if(dataDirs.size() > 0) {
|
||||
QDir dir(QDir(dataDirs.at(0)).filePath(".QtWebKit"));
|
||||
qDebug() << dir.path();
|
||||
if (dir.exists())
|
||||
dir.removeRecursively();
|
||||
}
|
||||
#else
|
||||
QString value = QDir(QDesktopServices::storageLocation(QDesktopServices::DataLocation)).path();
|
||||
#endif
|
||||
value = value + "/.QtWebKit";
|
||||
removeDir(value);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Utils::log(const QString & data)
|
||||
|
|
|
|||