Skip to content

Commit

Permalink
Activity list fixes and improvements. Adjusted sorting to show intera…
Browse files Browse the repository at this point in the history
…ctive and security activities always on top (after errors). Added button to scroll up when new activity arrives. Improved sync status scrollbar.

Signed-off-by: alex-z <[email protected]>
  • Loading branch information
allexzander committed Sep 22, 2023
1 parent 395edd9 commit 15cf06e
Show file tree
Hide file tree
Showing 18 changed files with 361 additions and 62 deletions.
1 change: 1 addition & 0 deletions resources.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
<file>src/gui/tray/EditFileLocallyLoadingDialog.qml</file>
<file>src/gui/tray/NCBusyIndicator.qml</file>
<file>src/gui/tray/NCToolTip.qml</file>
<file>src/gui/tray/NCProgressBar.qml</file>
<file>src/gui/tray/EnforcedPlainTextLabel.qml</file>
<file>theme/Style/Style.qml</file>
<file>theme/Style/qmldir</file>
Expand Down
2 changes: 1 addition & 1 deletion src/gui/folderman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1314,7 +1314,7 @@ QStringList FolderMan::findFileInLocalFolders(const QString &relPath, const Acco
if (acc && folder->accountState()->account() != acc) {
continue;
}
if (!serverPath.startsWith(folder->remotePath()))
if (!serverPath.startsWith(folder->remotePathTrailingSlash()))
continue;

QString path = folder->cleanPath() + '/';
Expand Down
26 changes: 26 additions & 0 deletions src/gui/tray/ActivityList.qml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,19 @@ import com.nextcloud.desktopclient 1.0 as NC
ScrollView {
id: controlRoot
property alias model: sortedActivityList.sourceModel
property alias count: activityList.count
property alias atYBeginning : activityList.atYBeginning
property bool isFileActivityList: false
property int iconSize: Style.trayListItemIconSize
property int delegateHorizontalPadding: 0

property bool scrollingToTop: false

function scrollToTop() {
// Triggers activation of repeating upward flick timer
scrollingToTop = true
}

signal openFile(string filePath)
signal activityItemClicked(int index)

Expand All @@ -22,6 +31,9 @@ ScrollView {

data: NC.WheelHandler {
target: controlRoot.contentItem
onWheel: {
scrollingToTop = false
}
}

ListView {
Expand All @@ -36,6 +48,20 @@ ScrollView {
currentIndex: -1
interactive: true

Timer {
id: repeatUpFlickTimer
interval: Style.activityListScrollToTopTimerInterval
running: controlRoot.scrollingToTop
repeat: true
onTriggered: {
if (!activityList.atYBeginning) {
activityList.flick(0, Style.activityListScrollToTopVelocity)
} else {
controlRoot.scrollingToTop = false
}
}
}

highlight: Rectangle {
id: activityHover
anchors.fill: activityList.currentItem
Expand Down
44 changes: 44 additions & 0 deletions src/gui/tray/NCProgressBar.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2023 by Oleksandr Zolotov <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/

import QtQuick 2.15
import QtQuick.Controls 2.15
import Style 1.0

ProgressBar {
id: control

background: Rectangle {
implicitWidth: Style.progressBarWidth
implicitHeight: Style.progressBarBackgroundHeight
radius: Style.progressBarRadius
color: Style.progressBarBackgroundColor
border.color: Style.progressBarBackgroundBorderColor
border.width: Style.progressBarBackgroundBorderWidth
}

contentItem: Item {
implicitWidth: Style.progressBarWidth
implicitHeight: Style.progressBarContentHeight

Rectangle {
width: control.visualPosition * parent.width
height: parent.height
radius: Style.progressBarRadius
color: Style.progressBarContentColor
border.color: Style.progressBarContentBorderColor
border.width: Style.progressBarContentBorderWidth
}
}
}
31 changes: 4 additions & 27 deletions src/gui/tray/SyncStatus.qml
Original file line number Diff line number Diff line change
Expand Up @@ -61,36 +61,13 @@ RowLayout {

Loader {
Layout.fillWidth: true
Layout.preferredHeight: Style.progressBarPreferredHeight

active: syncStatus.syncing
visible: syncStatus.syncing
active: syncStatus.syncing && syncStatus.totalFiles > 0
visible: active

sourceComponent: ProgressBar {
sourceComponent: NCProgressBar {
id: syncProgressBar

// TODO: Rather than setting all these palette colours manually,
// create a custom style and do it for all components globally.
//
// Additionally, we need to override the entire palette when we
// set one palette property, as otherwise we default back to the
// theme palette -- not the parent palette
palette {
text: Style.ncTextColor
windowText: Style.ncTextColor
buttonText: Style.ncTextColor
brightText: Style.ncTextBrightColor
highlight: Style.lightHover
highlightedText: Style.ncTextColor
light: Style.lightHover
midlight: Style.ncSecondaryTextColor
mid: Style.darkerHover
dark: Style.menuBorder
button: Style.buttonBackgroundColor
window: palette.dark // NOTE: Fusion theme uses darker window colour for the border of the progress bar
base: Style.backgroundColor
toolTipBase: Style.backgroundColor
toolTipText: Style.ncTextColor
}
value: syncStatus.syncProgress
}
}
Expand Down
70 changes: 70 additions & 0 deletions src/gui/tray/Window.qml
Original file line number Diff line number Diff line change
Expand Up @@ -851,7 +851,69 @@ ApplicationWindow {
anchors.right: trayWindowMainItem.right
}

Loader {
id: newActivitiesButtonLoader

anchors.top: activityList.top
anchors.topMargin: 5
anchors.horizontalCenter: activityList.horizontalCenter

width: Style.newActivitiesButtonWidth
height: Style.newActivitiesButtonHeight

z: 1

active: false

sourceComponent: CustomButton {
id: newActivitiesButton
hoverEnabled: true
padding: Style.smallSpacing

textColor: Style.currentUserHeaderTextColor
textColorHovered: Style.currentUserHeaderTextColor
contentsFont.bold: true
bgNormalColor: Qt.lighter(bgHoverColor, 1.25)
bgHoverColor: Style.currentUserHeaderColor
bgNormalOpacity: Style.newActivitiesBgNormalOpacity
bgHoverOpacity: Style.newActivitiesBgHoverOpacity

anchors.fill: parent

text: qsTr("New activities")

icon.source: "image://svgimage-custom-color/expand-less-black.svg" + "/" + Style.currentUserHeaderTextColor
icon.width: Style.activityLabelBaseWidth
icon.height: Style.activityLabelBaseWidth

onClicked: {
activityList.scrollToTop();
newActivitiesButtonLoader.active = false
}

Timer {
id: newActivitiesButtonDisappearTimer
interval: Style.newActivityButtonDisappearTimeout
running: newActivitiesButtonLoader.active && !newActivitiesButton.hovered
repeat: false
onTriggered: fadeoutActivitiesButtonDisappear.running = true
}

OpacityAnimator {
id: fadeoutActivitiesButtonDisappear
target: newActivitiesButton;
from: 1;
to: 0;
duration: Style.newActivityButtonDisappearFadeTimeout
loops: 1
running: false
onFinished: newActivitiesButtonLoader.active = false
}
}
}

ActivityList {
id: activityList
visible: !trayWindowMainItem.isUnifiedSearchActive
anchors.top: syncStatus.bottom
anchors.left: trayWindowMainItem.left
Expand All @@ -864,6 +926,14 @@ ApplicationWindow {
onActivityItemClicked: {
model.slotTriggerDefaultAction(index)
}
Connections {
target: activityModel
onInteractiveActivityReceived: {
if (!activityList.atYBeginning) {
newActivitiesButtonLoader.active = true;
}
}
}
}
} // Item trayWindowMainItem
}
11 changes: 9 additions & 2 deletions src/gui/tray/activitylistmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
if (!fileName.isEmpty()) {
const auto folder = FolderMan::instance()->folder(a._folder);

const QString relPath = folder ? folder->remotePath() + fileName : fileName;
const QString relPath = folder ? folder->remotePathTrailingSlash() + fileName : fileName;

const auto localFiles = FolderMan::instance()->findFileInLocalFolders(relPath, ast->account());

Expand Down Expand Up @@ -184,7 +184,7 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
if (!a._file.isEmpty()) {
const auto folder = FolderMan::instance()->folder(a._folder);

QString relPath = folder ? folder->remotePath() + a._file : a._file;
QString relPath = folder ? folder->remotePathTrailingSlash() + a._file : a._file;

const auto localFiles = FolderMan::instance()->findFileInLocalFolders(relPath, ast->account());

Expand Down Expand Up @@ -636,6 +636,13 @@ void ActivityListModel::addNotificationToActivityList(const Activity &activity)
qCDebug(lcActivity) << "Notification successfully added to the notification list: " << activity._subject;
addEntriesToActivityList({activity});
_notificationLists.prepend(activity);
for (const auto &link : activity._links) {
if (link._verb == QByteArrayLiteral("POST")
|| link._verb == QByteArrayLiteral("REPLY")
|| link._verb == QByteArrayLiteral("WEB")) {
emit interactiveActivityReceived();
}
}
}

void ActivityListModel::addSyncFileItemToActivityList(const Activity &activity)
Expand Down
2 changes: 2 additions & 0 deletions src/gui/tray/activitylistmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ public slots:
void activityJobStatusCode(int statusCode);
void sendNotificationRequest(const QString &accountName, const QString &link, const QByteArray &verb, int row);

void interactiveActivityReceived();

protected:
[[nodiscard]] bool currentlyFetching() const;

Expand Down
53 changes: 53 additions & 0 deletions src/gui/tray/sortedactivitylistmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,37 @@
*/

#include "activitylistmodel.h"
#include <QVector>

#include "sortedactivitylistmodel.h"

namespace
{
struct ActivityLinksSearchResult {
bool hasPOST = false;
bool hasREPLY = false;
bool hasWEB = false;
bool hasDELETE = false;
};

ActivityLinksSearchResult searchForVerbsInLinks(const QVector<OCC::ActivityLink> &links)
{
ActivityLinksSearchResult result;
for (const auto &link : links) {
if (link._verb == QByteArrayLiteral("POST")) {
result.hasPOST = true;
} else if (link._verb == QByteArrayLiteral("REPLY")) {
result.hasREPLY = true;
} else if (link._verb == QByteArrayLiteral("WEB")) {
result.hasWEB = true;
} else if (link._verb == QByteArrayLiteral("DELETE")) {
result.hasDELETE = true;
}
}
return result;
}
}

namespace OCC {

SortedActivityListModel::SortedActivityListModel(QObject *parent)
Expand Down Expand Up @@ -44,6 +72,31 @@ bool SortedActivityListModel::lessThan(const QModelIndex &sourceLeft, const QMod
return false;
}

const auto leftActivityVerbsSearchResult = searchForVerbsInLinks(leftActivity._links);
const auto rightActivityVerbsSearchResult = searchForVerbsInLinks(rightActivity._links);

if (leftActivityVerbsSearchResult.hasPOST != rightActivityVerbsSearchResult.hasPOST) {
return leftActivityVerbsSearchResult.hasPOST;
}

if (leftActivityVerbsSearchResult.hasREPLY != rightActivityVerbsSearchResult.hasREPLY) {
return leftActivityVerbsSearchResult.hasREPLY;
}

if (leftActivityVerbsSearchResult.hasWEB != rightActivityVerbsSearchResult.hasWEB) {
return leftActivityVerbsSearchResult.hasWEB;
}

if (leftActivityVerbsSearchResult.hasDELETE != rightActivityVerbsSearchResult.hasDELETE) {
return leftActivityVerbsSearchResult.hasDELETE;
}

const auto leftActivityIsSecurityAction = leftActivity._fileAction == QStringLiteral("security");
const auto rightActivityIsSecurityAction = rightActivity._fileAction == QStringLiteral("security");
if (leftActivityIsSecurityAction != rightActivityIsSecurityAction) {
return leftActivityIsSecurityAction;
}

// Let's now check for errors as we want those near the top too
// Sync result errors go first
const auto leftSyncResultStatus = leftActivity._syncResultStatus;
Expand Down
Loading

0 comments on commit 15cf06e

Please sign in to comment.