Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/gui/tray/CurrentAccountHeaderButton.qml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ Button {

property real widestMenuItemWidth: 0
property real maximumWidthAllowed: trayWindowHeader.width - (root.x + 4)
width: Math.min( widestMenuItemWidth + leftPadding + rightPadding, maximumWidthAllowed )
property real extraMenuWidth: Style.userLineSpacing
width: Math.min(widestMenuItemWidth + leftPadding + rightPadding + extraMenuWidth, maximumWidthAllowed)
height: Math.min(implicitHeight, maxMenuHeight)
closePolicy: Menu.CloseOnPressOutsideParent | Menu.CloseOnEscape

Expand All @@ -75,6 +76,7 @@ Button {
UserLine {
id: instantiatedUserLine
width: Math.min(accountMenu.widestMenuItemWidth, accountMenu.maximumWidthAllowed)
parentBackgroundColor: Style.colorWithoutTransparency(accountMenu.palette.window)

Component.onCompleted: {
instantiatedUserLine.updateMenuWidth()
Expand Down Expand Up @@ -265,8 +267,7 @@ Button {
&& UserModel.currentUser.status !== NC.userStatus.Offline
source: UserModel.currentUser ? UserModel.currentUser.statusIcon : ""
cache: false
x: currentAccountStatusIndicatorBackground.x + Style.trayFolderStatusIndicatorSizeOffset / 2
y: currentAccountStatusIndicatorBackground.y + Style.trayFolderStatusIndicatorSizeOffset / 2
anchors.centerIn: currentAccountStatusIndicatorBackground
sourceSize.width: Style.accountAvatarStateIndicatorSize
sourceSize.height: Style.accountAvatarStateIndicatorSize

Expand Down
35 changes: 32 additions & 3 deletions src/gui/tray/UserLine.qml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ AbstractButton {
signal showUserStatusSelector(int id)
signal showUserStatusMessageSelector(int id)

property color parentBackgroundColor: userLine.palette.base

Accessible.role: Accessible.MenuItem
Accessible.name: qsTr("Switch to account") + " " + model.name
Expand All @@ -42,7 +43,12 @@ AbstractButton {
visible: model.isConnected && model.serverHasUserStatus
width: accountStatusIndicator.sourceSize.width + Style.trayFolderStatusIndicatorSizeOffset
height: width
color: "white"
readonly property bool isHighlighted: userLine.parent && (userLine.parent.highlighted || userLine.parent.down)
readonly property color menuBaseColor: Style.colorWithoutTransparency(
userLine.parent && userLine.parent.palette ? userLine.parent.palette.base : userLine.parentBackgroundColor)
readonly property color menuHighlightColor: Style.colorWithoutTransparency(
userLine.parent && userLine.parent.palette ? userLine.parent.palette.highlight : userLine.palette.highlight)
color: (isHighlighted && Qt.platform.os !== "windows") ? menuHighlightColor : menuBaseColor
anchors.bottom: accountAvatar.bottom
anchors.right: accountAvatar.right
radius: width * Style.trayFolderStatusIndicatorRadiusFactor
Expand All @@ -53,8 +59,7 @@ AbstractButton {
visible: model.isConnected && model.serverHasUserStatus
source: model.statusIcon
cache: false
x: accountStatusIndicatorBackground.x + Style.trayFolderStatusIndicatorSizeOffset / 2
y: accountStatusIndicatorBackground.y + Style.trayFolderStatusIndicatorSizeOffset / 2
anchors.centerIn: accountStatusIndicatorBackground
sourceSize.width: Style.accountAvatarStateIndicatorSize
sourceSize.height: Style.accountAvatarStateIndicatorSize

Expand Down Expand Up @@ -143,6 +148,25 @@ AbstractButton {
Layout.fillWidth: true
}

Item {
id: syncStatusColumn
Layout.preferredWidth: Style.headerButtonIconSize
Layout.fillHeight: true

Image {
id: syncStatusIndicator
visible: !model.syncStatusOk
source: model.syncStatusIcon
cache: false
anchors.centerIn: parent
sourceSize.width: Style.accountAvatarStateIndicatorSize + Style.trayFolderStatusIndicatorSizeOffset
sourceSize.height: Style.accountAvatarStateIndicatorSize + Style.trayFolderStatusIndicatorSizeOffset

Accessible.role: Accessible.Indicator
Accessible.name: qsTr("Account sync status requires attention")
}
}

Button {
id: userMoreButton
Layout.preferredWidth: Style.headerButtonIconSize
Expand Down Expand Up @@ -236,3 +260,8 @@ AbstractButton {
}
} // MenuItem userLine






115 changes: 113 additions & 2 deletions src/gui/tray/usermodel.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/*
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-2.0-or-later
Expand All @@ -18,6 +18,7 @@
#include "notificationconfirmjob.h"
#include "logger.h"
#include "guiutility.h"
#include "syncresult.h"

Check warning on line 21 in src/gui/tray/usermodel.cpp

View workflow job for this annotation

GitHub Actions / build

src/gui/tray/usermodel.cpp:21:1 [readability-duplicate-include]

duplicate include
#include "syncfileitem.h"
#include "systray.h"
#include "tray/activitylistmodel.h"
Expand All @@ -39,6 +40,7 @@
#include <QPainter>
#include <QPushButton>
#include <QDateTime>
#include <theme.h>

// time span in milliseconds which has to be between two
// refreshes of the notifications
Expand All @@ -48,6 +50,11 @@
constexpr qint64 expiredActivitiesCheckIntervalMsecs = 1000 * 60;
constexpr qint64 activityDefaultExpirationTimeMsecs = 1000 * 60 * 10;

struct SyncStatusInfo {

Check warning on line 53 in src/gui/tray/usermodel.cpp

View workflow job for this annotation

GitHub Actions / build

src/gui/tray/usermodel.cpp:53:8 [cppcoreguidelines-pro-type-member-init]

constructor does not initialize these fields: icon
QUrl icon;
bool ok = true;
};

OCC::SyncResult::Status determineSyncStatus(const OCC::SyncResult &syncResult)
{
const auto status = syncResult.status();
Expand All @@ -63,6 +70,62 @@
return status;
}

SyncStatusInfo syncStatusForAccount(const OCC::AccountStatePtr &accountState)

Check warning on line 73 in src/gui/tray/usermodel.cpp

View workflow job for this annotation

GitHub Actions / build

src/gui/tray/usermodel.cpp:73:16 [modernize-use-trailing-return-type]

use a trailing return type for this function
{
if (!accountState || !accountState->isConnected()) {
return {OCC::Theme::instance()->offline(), false};
}

bool hasError = false;
bool hasWarning = false;
bool hasPaused = false;
bool hasSyncing = false;
for (const auto &folder : OCC::FolderMan::instance()->map()) {
if (!folder || folder->accountState() != accountState.data()) {
continue;
}

const auto state = determineSyncStatus(folder->syncResult());

switch (state) {
case OCC::SyncResult::Error:
case OCC::SyncResult::SetupError:
hasError = true;
break;
case OCC::SyncResult::Problem:
case OCC::SyncResult::Undefined:
hasWarning = true;
break;
case OCC::SyncResult::Paused:
case OCC::SyncResult::SyncAbortRequested:
hasPaused = true;
break;
case OCC::SyncResult::SyncRunning:
case OCC::SyncResult::NotYetStarted:
hasSyncing = true;
break;
case OCC::SyncResult::Success:
case OCC::SyncResult::SyncPrepare:
break;
}
}

if (hasError) {
return {OCC::Theme::instance()->error(), false};
}
if (hasWarning) {
return {OCC::Theme::instance()->warning(), false};
}
if (hasPaused) {
return {OCC::Theme::instance()->pause(), false};
}
if (hasSyncing) {
return {OCC::Theme::instance()->sync(), false};
}

return {OCC::Theme::instance()->ok(), true};
}

bool isSyncStatusError(const OCC::SyncResult::Status status)
{
switch (status) {
Expand All @@ -81,10 +144,11 @@
}
return false;
}
}

} // namespace

namespace OCC {

TrayFolderInfo::TrayFolderInfo(const QString &name, const QString &parentPath, const QString &fullPath, FolderType folderType)
: _name(name)
, _parentPath(parentPath)
Expand Down Expand Up @@ -162,6 +226,18 @@
showDesktopNotification(certificateNeedMigration);
}
});

const auto folderMan = FolderMan::instance();
connect(folderMan, &FolderMan::folderSyncStateChange, this, [this](const Folder *folder) {
if (!folder || folder->accountState() == _account.data()) {
updateSyncStatus();
}
});
connect(folderMan, &FolderMan::folderListChanged, this, [this](const Folder::Map &) {
updateSyncStatus();
});
connect(_account.data(), &AccountState::isConnectedChanged, this, &User::updateSyncStatus);
updateSyncStatus();
}

void User::checkNotifiedNotifications()
Expand Down Expand Up @@ -1121,6 +1197,28 @@
return _account->account()->userStatusConnector()->userStatus().icon();
}

QUrl User::syncStatusIcon() const

Check warning on line 1200 in src/gui/tray/usermodel.cpp

View workflow job for this annotation

GitHub Actions / build

src/gui/tray/usermodel.cpp:1200:12 [modernize-use-trailing-return-type]

use a trailing return type for this function
{
return _syncStatusIcon;
}

bool User::syncStatusOk() const

Check warning on line 1205 in src/gui/tray/usermodel.cpp

View workflow job for this annotation

GitHub Actions / build

src/gui/tray/usermodel.cpp:1205:12 [modernize-use-trailing-return-type]

use a trailing return type for this function
{
return _syncStatusOk;
}

void User::updateSyncStatus()

Check warning on line 1210 in src/gui/tray/usermodel.cpp

View workflow job for this annotation

GitHub Actions / build

src/gui/tray/usermodel.cpp:1210:12 [readability-convert-member-functions-to-static]

method 'updateSyncStatus' can be made static
{
const auto info = syncStatusForAccount(_account);
if (_syncStatusIcon == info.icon && _syncStatusOk == info.ok) {
return;
}

_syncStatusIcon = info.icon;
_syncStatusOk = info.ok;
emit syncStatusChanged();

Check warning on line 1219 in src/gui/tray/usermodel.cpp

View workflow job for this annotation

GitHub Actions / build

src/gui/tray/usermodel.cpp:1219:10 [modernize-use-trailing-return-type]

use a trailing return type for this function
}

bool User::serverHasUserStatus() const
{
return _account->account()->capabilities().userStatus();
Expand Down Expand Up @@ -1558,6 +1656,11 @@
});
connect(u, &User::accountStateChanged, this, &UserModel::updateSyncErrorUsers);

connect(u, &User::syncStatusChanged, this, [this, row] {
emit dataChanged(index(row, 0), index(row, 0), { UserModel::SyncStatusIconRole,

Check warning on line 1660 in src/gui/tray/usermodel.cpp

View workflow job for this annotation

GitHub Actions / build

src/gui/tray/usermodel.cpp:1660:18 [cppcoreguidelines-init-variables]

variable 'dataChanged' is not initialized
UserModel::SyncStatusOkRole });
});

_users << u;
if (isCurrent || (_currentUserId < 0 && !_init)) {
setCurrentUserId(_users.size() - 1);
Expand Down Expand Up @@ -1741,7 +1844,7 @@
auto result = QVariant{};
switch (static_cast<UserRoles>(role))
{
case NameRole:

Check warning on line 1847 in src/gui/tray/usermodel.cpp

View workflow job for this annotation

GitHub Actions / build

src/gui/tray/usermodel.cpp:1847:5 [bugprone-branch-clone]

switch has 16 consecutive identical branches
result = _users[index.row()]->name();
break;
case ServerRole:
Expand Down Expand Up @@ -1783,6 +1886,12 @@
case RemoveAccountTextRole:
result = _users[index.row()]->isPublicShareLink() ? tr("Leave share") : tr("Remove account");
break;
case SyncStatusIconRole:
result = _users[index.row()]->syncStatusIcon();
break;
case SyncStatusOkRole:
result = _users[index.row()]->syncStatusOk();
break;
}

return result;
Expand All @@ -1805,6 +1914,8 @@
roles[IdRole] = "id";
roles[CanLogoutRole] = "canLogout";
roles[RemoveAccountTextRole] = "removeAccountText";
roles[SyncStatusIconRole] = "syncStatusIcon";
roles[SyncStatusOkRole] = "syncStatusOk";
return roles;
}

Expand Down
10 changes: 10 additions & 0 deletions src/gui/tray/usermodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#ifndef USERMODEL_H
#define USERMODEL_H

#include <QAbstractListModel>

Check failure on line 9 in src/gui/tray/usermodel.h

View workflow job for this annotation

GitHub Actions / build

src/gui/tray/usermodel.h:9:10 [clang-diagnostic-error]

'QAbstractListModel' file not found
#include <QImage>
#include <QDateTime>
#include <QStringList>
Expand Down Expand Up @@ -68,6 +68,8 @@
Q_PROPERTY(QString featuredAppIcon READ featuredAppIcon NOTIFY featuredAppChanged)
Q_PROPERTY(QString featuredAppAccessibleName READ featuredAppAccessibleName NOTIFY featuredAppChanged)
Q_PROPERTY(QString avatar READ avatarUrl NOTIFY avatarChanged)
Q_PROPERTY(QUrl syncStatusIcon READ syncStatusIcon NOTIFY syncStatusChanged)
Q_PROPERTY(bool syncStatusOk READ syncStatusOk NOTIFY syncStatusChanged)
Q_PROPERTY(bool isConnected READ isConnected NOTIFY accountStateChanged)
Q_PROPERTY(bool needsToSignTermsOfService READ needsToSignTermsOfService NOTIFY accountStateChanged)
Q_PROPERTY(UnifiedSearchResultsListModel* unifiedSearchResultsListModel READ getUnifiedSearchResultsListModel CONSTANT)
Expand Down Expand Up @@ -113,6 +115,8 @@
[[nodiscard]] QString statusMessage() const;
[[nodiscard]] QUrl statusIcon() const;
[[nodiscard]] QString statusEmoji() const;
[[nodiscard]] QUrl syncStatusIcon() const;
[[nodiscard]] bool syncStatusOk() const;
void processCompletedSyncItem(const Folder *folder, const SyncFileItemPtr &item);
[[nodiscard]] const QVariantList &groupFolders() const;
[[nodiscard]] bool canLogout() const;
Expand All @@ -129,6 +133,7 @@
void headerColorChanged();
void headerTextColorChanged();
void accentColorChanged();
void syncStatusChanged();
void sendReplyMessage(const int activityIndex, const QString &conversationToken, const QString &message, const QString &replyTo);
void groupFoldersChanged();

Expand Down Expand Up @@ -183,6 +188,7 @@

bool isActivityOfCurrentAccount(const Folder *folder) const;
[[nodiscard]] bool isUnsolvableConflict(const SyncFileItemPtr &item) const;
void updateSyncStatus();

bool notificationAlreadyShown(const qint64 notificationId);
bool canShowNotification(const qint64 notificationId);
Expand Down Expand Up @@ -216,6 +222,8 @@
// used for quota warnings
int _lastQuotaPercent = 0;
Activity _lastQuotaActivity;
QUrl _syncStatusIcon;
bool _syncStatusOk = true;
};

class UserModel : public QAbstractListModel
Expand Down Expand Up @@ -273,6 +281,8 @@
IdRole,
CanLogoutRole,
RemoveAccountTextRole,
SyncStatusIconRole,
SyncStatusOkRole,
};

[[nodiscard]] AccountAppList appList() const;
Expand Down
Loading