526 lines
18 KiB
QML
526 lines
18 KiB
QML
/* Copyright (C) 2014-2022 Michal Kosciesza <michal@mkiol.net>
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
import QtQuick 2.0
|
|
import Sailfish.Silica 1.0
|
|
|
|
import harbour.kaktus.Settings 1.0
|
|
|
|
Page {
|
|
id: root
|
|
|
|
objectName: "entries"
|
|
|
|
allowedOrientations: {
|
|
switch (settings.allowedOrientations) {
|
|
case 1:
|
|
return Orientation.PortraitMask;
|
|
case 2:
|
|
return Orientation.LandscapeMask;
|
|
}
|
|
return Orientation.All;
|
|
}
|
|
|
|
property bool showBar: true
|
|
property alias remorse: _remorse
|
|
property string title
|
|
property bool landscapeMode: settings.doublePane && (app.isTablet || root.orientation === Orientation.Landscape)
|
|
property EntryDelegate expandedDelegate
|
|
property string expandedUid: ""
|
|
property int expandedIndex: 0
|
|
|
|
function openBrowser(index, link, uid) {
|
|
entryModel.setData(index, "read", 1, "");
|
|
Qt.openUrlExternally(link);
|
|
}
|
|
|
|
function setContentPane(delegate) {
|
|
//console.log("setContentPane",delegate);
|
|
contentPanel.index = delegate.index
|
|
//contentPanel.content = app.isTablet ? delegate.contentraw : delegate.contentall;
|
|
contentPanel.content = delegate.contentall;
|
|
//contentPanel.image = app.isTablet ? "" : delegate.image;
|
|
contentPanel.image = delegate.image;
|
|
contentPanel.expanded = false;
|
|
delegate.expanded = true;
|
|
}
|
|
|
|
function getDelegateByUid(uid) {
|
|
for (var i = 0; i < listView.contentItem.children.length; i++) {
|
|
curItem = listView.contentItem.children[i];
|
|
if (curItem.objectName === "EntryDelegate" && !curItem.last && !curItem.daterow &&
|
|
curItem.uid === uid) {
|
|
return curItem;
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
function clearContentPane(delegate) {
|
|
if (delegate) {
|
|
if (contentPanel.index === delegate.index) {
|
|
contentPanel.index = 0;
|
|
contentPanel.content = "";
|
|
contentPanel.image = "";
|
|
contentPanel.expanded = false;
|
|
}
|
|
} else {
|
|
contentPanel.index = 0;
|
|
contentPanel.content = "";
|
|
contentPanel.image = "";
|
|
contentPanel.expanded = false;
|
|
}
|
|
}
|
|
|
|
function autoSetDelegate() {
|
|
var delegate = root.expandedDelegate ? root.expandedDelegate : root.expandedUid!="" ? getDelegateByUid(root.expandedUid) : undefined;
|
|
//console.log("autoSetDelegate",delegate);
|
|
if (!delegate) {
|
|
var curItem = listView.itemAt(0,listView.contentY + root.height/4);
|
|
if (curItem.objectName === "EntryDelegate" && !curItem.last && !curItem.daterow) {
|
|
curItem.expanded = true;
|
|
//expandedDelegate = curItem;
|
|
//expandedUid = curItem.uid;
|
|
//expandedIndex = curItem.index;
|
|
return;
|
|
} else {
|
|
for (var i = 0; i < listView.contentItem.children.length; i++) {
|
|
curItem = listView.contentItem.children[i];
|
|
if (curItem.objectName === "EntryDelegate" && !curItem.last && !curItem.daterow) {
|
|
curItem.expanded = true;
|
|
//expandedDelegate = curItem;
|
|
//expandedUid = curItem.uid;
|
|
//expandedIndex = curItem.index;
|
|
//listView.positionViewAtIndex(curItem.index, ListView.Contain);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
onExpandedUidChanged: {
|
|
//console.log("onExpandedUidChanged",root.expandedUid, root.expandedDelegate, root.expandedIndex);
|
|
var delegate = root.expandedDelegate ? root.expandedDelegate : root.expandedUid!="" ? getDelegateByUid(root.expandedUid) : undefined;
|
|
//console.log("delegate",delegate);
|
|
if (delegate) {
|
|
setContentPane(delegate);
|
|
} else {
|
|
clearContentPane(delegate);
|
|
}
|
|
}
|
|
|
|
onStatusChanged: {
|
|
if (status === PageStatus.Active) {
|
|
if (landscapeMode)
|
|
autoSetDelegate();
|
|
}
|
|
}
|
|
|
|
onOrientationTransitionRunningChanged: {
|
|
if (!orientationTransitionRunning) {
|
|
//console.log("onOrientationTransitionRunningChanged");
|
|
if (landscapeMode) {
|
|
autoSetDelegate();
|
|
} else {
|
|
var delegate = root.expandedDelegate ? root.expandedDelegate : root.expandedUid!="" ? getDelegateByUid(root.expandedUid) : undefined;
|
|
if (delegate)
|
|
listView.positionViewAtIndex(delegate.index, ListView.Contain);
|
|
}
|
|
}
|
|
}
|
|
|
|
ActiveDetector {
|
|
onInit: { bar.flick = listView }
|
|
}
|
|
|
|
RemorsePopup {
|
|
id: _remorse
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
color: Theme.rgba(Theme.highlightDimmerColor, 0.2)
|
|
}
|
|
|
|
SilicaListView {
|
|
id: listView
|
|
model: entryModel
|
|
|
|
anchors { top: parent.top; left: parent.left }
|
|
width: root.landscapeMode && listView.count != 0 ?
|
|
parent.width - app.landscapeContentPanelWidth : parent.width
|
|
clip: true
|
|
height: app.flickHeight
|
|
Behavior on height {NumberAnimation { duration: 200; easing.type: Easing.OutQuad }}
|
|
|
|
onContentYChanged: {
|
|
if (root.landscapeMode) {
|
|
var itemTop = itemAt(0,contentY + root.height/5)
|
|
var itemBottom = itemAt(0,contentY + 4*root.height/5)
|
|
if (!itemTop.last && !itemTop.daterow) {
|
|
if (root.expandedDelegate) {
|
|
if (root.expandedDelegate.index < itemTop.index ||
|
|
root.expandedDelegate.index > itemBottom.index )
|
|
itemTop.expanded = true
|
|
else
|
|
return
|
|
} else {
|
|
itemTop.expanded = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
PageMenu {
|
|
id: menu
|
|
showAbout: true
|
|
}
|
|
|
|
header: PageHeader {
|
|
title: {
|
|
switch (settings.viewMode) {
|
|
case Settings.AllEntries:
|
|
return qsTr("All feeds")
|
|
case Settings.SavedEntries:
|
|
return app.isNetvibes ? qsTr("Saved") : qsTr("Starred")
|
|
case Settings.SlowEntries:
|
|
return qsTr("Slow")
|
|
case Settings.LikedEntries:
|
|
return qsTr("Liked")
|
|
case Settings.BroadcastedEntries:
|
|
return qsTr("Shared")
|
|
default:
|
|
return root.title
|
|
}
|
|
}
|
|
}
|
|
|
|
delegate: EntryDelegate {
|
|
id: delegate
|
|
uid: model.uid
|
|
title: model.title
|
|
content: model.content
|
|
contentall: model.contentall
|
|
contentraw: model.contentraw
|
|
date: model.date
|
|
read: model.read
|
|
friendStream: model.feedId.substring(0,4) === "user"
|
|
feedIcon: model.feedIcon
|
|
feedTitle: model.feedTitle
|
|
author: model.author
|
|
image: model.image
|
|
readlater: model.readlater
|
|
index: model.index
|
|
cached: model.cached
|
|
broadcast: model.broadcast
|
|
liked: model.liked
|
|
annotations: model.annotations
|
|
fresh: model.fresh
|
|
last: model.uid === "last"
|
|
daterow: model.uid === "daterow"
|
|
showMarkedAsRead: settings.viewMode !== Settings.SavedEntries &&
|
|
settings.viewMode !== Settings.LikedEntries &&
|
|
settings.viewMode !== Settings.BroadcastedEntries &&
|
|
model.read<2
|
|
objectName: "EntryDelegate"
|
|
landscapeMode: root.landscapeMode
|
|
onlineurl: model.link
|
|
offlineurl: cserver.getUrlbyId(model.uid)
|
|
|
|
signal singleEntryClicked
|
|
|
|
function check() {
|
|
// Not allowed while Syncing
|
|
if (dm.busy || fetcher.busy || dm.removerBusy) {
|
|
notification.show(qsTr("Wait until current task is complete"));
|
|
return false
|
|
}
|
|
|
|
// Entry not cached and offline mode enabled
|
|
if (settings.offlineMode && !model.cached) {
|
|
notification.show(qsTr("Offline version is not available"));
|
|
return false
|
|
}
|
|
|
|
// Switch to offline mode if no network
|
|
if (!settings.offlineMode && !dm.online) {
|
|
if (model.cached) {
|
|
// Entry cached
|
|
notification.show(qsTr("Enabling offline mode because network is disconnected"));
|
|
settings.offlineMode = true;
|
|
} else {
|
|
// Entry not cached
|
|
notification.show(qsTr("Network is disconnected"));
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
function openEntryInViewer() {
|
|
pageStack.push(Qt.resolvedUrl("WebPreviewPage.qml"),
|
|
{"entryId": model.uid,
|
|
"onlineUrl": delegate.onlineurl,
|
|
"offlineUrl": delegate.offlineurl,
|
|
"title": model.title,
|
|
"stared": model.readlater===1,
|
|
"liked": model.liked,
|
|
"broadcast": model.broadcast,
|
|
"index": model.index,
|
|
"feedindex": root.index,
|
|
"read" : model.read===1,
|
|
"cached" : model.cached
|
|
})
|
|
}
|
|
|
|
function showEntryFeedContent() {
|
|
// Not allowed while Syncing
|
|
if (dm.busy || fetcher.busy || dm.removerBusy) {
|
|
notification.show(qsTr("Wait until current task is complete"))
|
|
return false
|
|
}
|
|
|
|
pageStack.push(Qt.resolvedUrl("FeedWebContentPage.qml"),
|
|
{"entryId": model.uid,
|
|
"content": model.contentraw,
|
|
"onlineUrl": delegate.onlineurl,
|
|
"offlineUrl": delegate.offlineurl,
|
|
"title": model.title,
|
|
"stared": model.readlater===1,
|
|
"liked": model.liked,
|
|
"broadcast": model.broadcast,
|
|
"index": model.index,
|
|
"feedindex": root.index,
|
|
"read" : model.read===1,
|
|
"cached" : model.cached
|
|
})
|
|
}
|
|
|
|
function openEntry() {
|
|
if (settings.clickBehavior === 2) {
|
|
showEntryFeedContent();
|
|
return
|
|
}
|
|
|
|
if (!check()) {
|
|
return
|
|
}
|
|
|
|
if (settings.clickBehavior === 1) {
|
|
openBrowser(model.index, model.link, model.uid);
|
|
return
|
|
}
|
|
|
|
openEntryInViewer()
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
//Dynamic creation of new items if last item is compleated
|
|
if (index==entryModel.count()-2) {
|
|
entryModel.createItems(index+2,settings.offsetLimit)
|
|
}
|
|
}
|
|
|
|
onClicked: {
|
|
timer.start()
|
|
}
|
|
|
|
onDoubleClicked: {
|
|
timer.stop()
|
|
if (model.read === 0) {
|
|
entryModel.setData(model.index, "read", 1, "")
|
|
//read = 1;
|
|
} else {
|
|
entryModel.setData(model.index, "read", 0, "")
|
|
//read = 0;
|
|
}
|
|
}
|
|
|
|
onSingleEntryClicked: {
|
|
// Landscape mode
|
|
if (root.landscapeMode) {
|
|
delegate.expanded = true
|
|
return
|
|
}
|
|
|
|
// Portrait mode
|
|
openEntry()
|
|
}
|
|
|
|
Timer {
|
|
id: timer
|
|
interval: 200
|
|
onTriggered: {
|
|
// Single click
|
|
singleEntryClicked()
|
|
}
|
|
}
|
|
|
|
onExpandedChanged: {
|
|
if (expanded) {
|
|
// Collapsing all other items on expand
|
|
for (var i = 0; i < listView.contentItem.children.length; i++) {
|
|
var curItem = listView.contentItem.children[i]
|
|
if (curItem !== delegate) {
|
|
if (curItem.objectName==="EntryDelegate") {
|
|
if (curItem.expanded)
|
|
curItem.expanded = false
|
|
}
|
|
}
|
|
}
|
|
|
|
root.expandedDelegate = delegate
|
|
root.expandedUid = delegate.uid
|
|
root.expandedIndex = delegate.index
|
|
} else {
|
|
if (delegate === root.expandedDelegate) {
|
|
root.expandedDelegate = null
|
|
root.expandedUid = ""
|
|
root.expandedIndex = 0
|
|
}
|
|
}
|
|
}
|
|
|
|
onMarkedAsRead: {
|
|
entryModel.setData(model.index, "read", 1, "")
|
|
}
|
|
|
|
onMarkedAsUnread: {
|
|
entryModel.setData(model.index, "read", 0, "")
|
|
}
|
|
|
|
onMarkedReadlater: {
|
|
entryModel.setData(index, "readlater", 1, "")
|
|
if (evaluation !== -1) {
|
|
evaluation = 1
|
|
ai.addEvaluation(model.uid, model.title, evaluation)
|
|
}
|
|
}
|
|
|
|
onUnmarkedReadlater: {
|
|
entryModel.setData(index, "readlater", 0, "")
|
|
}
|
|
|
|
onMarkedLike: {
|
|
entryModel.setData(model.index, "liked", true, "")
|
|
}
|
|
|
|
onUnmarkedLike: {
|
|
entryModel.setData(model.index, "liked", false, "")
|
|
}
|
|
|
|
onMarkedBroadcast: {
|
|
pageStack.push(Qt.resolvedUrl("ShareDialog.qml"),{"index": model.index,})
|
|
}
|
|
|
|
onUnmarkedBroadcast: {
|
|
entryModel.setData(model.index, "broadcast", false, "")
|
|
}
|
|
|
|
onMarkedAboveAsRead: {
|
|
entryModel.setAboveAsRead(model.index)
|
|
}
|
|
|
|
onShowFeedContent: {
|
|
showEntryFeedContent()
|
|
}
|
|
|
|
onOpenInBrowser: {
|
|
if (!check()) {
|
|
return
|
|
}
|
|
|
|
openBrowser(model.index, model.link, model.uid)
|
|
}
|
|
|
|
onOpenInViewer: {
|
|
if (!check()) {
|
|
return
|
|
}
|
|
|
|
openEntryInViewer()
|
|
}
|
|
|
|
onShare: {
|
|
pageStack.push(Qt.resolvedUrl("ShareLinkPage.qml"),{"link": model.link, "linkTitle": model.title})
|
|
}
|
|
|
|
onPocketAdd: {
|
|
pocket.add(model.link, model.title)
|
|
}
|
|
|
|
onSaveImage: {
|
|
fetcher.saveImage(model.image)
|
|
}
|
|
}
|
|
|
|
ViewPlaceholder {
|
|
id: placeholder
|
|
enabled: listView.count === 0
|
|
text: fetcher.busy ? qsTr("Wait until sync finish") :
|
|
settings.viewMode === Settings.SavedEntries ? app.isNetvibes ? qsTr("No saved items") : qsTr("No starred items") :
|
|
settings.viewMode === Settings.LikedEntries ? qsTr("No liked items") :
|
|
settings.showOnlyUnread ? qsTr("No unread items") : qsTr("No items")
|
|
}
|
|
|
|
VerticalScrollDecorator {
|
|
flickable: listView
|
|
}
|
|
}
|
|
|
|
EntryPageContent {
|
|
id: contentPanel
|
|
property bool expanded: false
|
|
|
|
visible: root.landscapeMode && active
|
|
anchors.right: root.right; anchors.top: root.top
|
|
width: expanded ? root.width : app.landscapeContentPanelWidth
|
|
clip: true
|
|
height: app.flickHeight
|
|
openable: false
|
|
textFormat: app.isTablet ? Text.StyledText : Text.PlainText
|
|
|
|
onClicked: {
|
|
var delegate = root.expandedDelegate ?
|
|
root.expandedDelegate : root.expandedUid.length > 0 ?
|
|
getDelegateByUid(root.expandedUid) : undefined
|
|
if (delegate)
|
|
delegate.openEntry()
|
|
}
|
|
|
|
onOpenClicked: {
|
|
var delegate = root.expandedDelegate ?
|
|
root.expandedDelegate : root.expandedUid.length > 0 ?
|
|
getDelegateByUid(root.expandedUid) : undefined
|
|
if (delegate)
|
|
delegate.openEntry()
|
|
}
|
|
|
|
busy: (width != root.width) &&
|
|
(width !== app.landscapeContentPanelWidth)
|
|
}
|
|
|
|
HintLabel {
|
|
anchors.bottom: parent.bottom
|
|
backgroundColor: Theme.highlightDimmerColor
|
|
Behavior on opacity { FadeAnimation { duration: 400 } }
|
|
opacity: bar.open || settings.getHint1Done() ? 0.0 : 1.0
|
|
visible: opacity != 0
|
|
|
|
text: qsTr("One-tap to open article, double-tap to mark as read")
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
onPressed: {
|
|
settings.setHint1Done(true);
|
|
parent.opacity = 0.0;
|
|
}
|
|
}
|
|
}
|
|
}
|