Skip to content

Commit

Permalink
Merge pull request #3069 from nextcloud/feature/user-status
Browse files Browse the repository at this point in the history
User status bug fixing...
  • Loading branch information
allexzander authored Apr 8, 2021
2 parents 835a61c + 1ca0ea4 commit f6afb62
Show file tree
Hide file tree
Showing 10 changed files with 89 additions and 92 deletions.
2 changes: 1 addition & 1 deletion src/gui/accountstate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ bool AccountState::isDesktopNotificationsAllowed() const
return _isDesktopNotificationsAllowed;
}

void AccountState::setDesktopNotificationsAllowed(const bool isAllowed)
void AccountState::setDesktopNotificationsAllowed(bool isAllowed)
{
_isDesktopNotificationsAllowed = isAllowed;
}
Expand Down
2 changes: 1 addition & 1 deletion src/gui/accountstate.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ class AccountState : public QObject, public QSharedData

/** Set desktop notifications status retrieved by the notificatons endpoint
*/
void setDesktopNotificationsAllowed(const bool isAllowed);
void setDesktopNotificationsAllowed(bool isAllowed);

/** Fetch the user status (status, icon, message)
*/
Expand Down
2 changes: 1 addition & 1 deletion src/gui/tray/NotificationHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ void ServerNotificationHandler::slotEtagResponseHeaderReceived(const QByteArray
}
}

void ServerNotificationHandler::slotAllowDesktopNotificationsChanged(const bool isAllowed)
void ServerNotificationHandler::slotAllowDesktopNotificationsChanged(bool isAllowed)
{
auto *account = qvariant_cast<AccountState *>(sender()->property(propertyAccountStateC));
if (account != nullptr) {
Expand Down
2 changes: 1 addition & 1 deletion src/gui/tray/NotificationHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ private slots:
void slotNotificationsReceived(const QJsonDocument &json, int statusCode);
void slotEtagResponseHeaderReceived(const QByteArray &value, int statusCode);
void slotIconDownloaded(QByteArray iconData);
void slotAllowDesktopNotificationsChanged(const bool isAllowed);
void slotAllowDesktopNotificationsChanged(bool isAllowed);

private:
QPointer<JsonApiJob> _notificationJob;
Expand Down
5 changes: 3 additions & 2 deletions src/gui/tray/UserModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,8 @@ void User::slotRefreshActivities()
_activityModel->slotRefreshActivity();
}

void User::slotRefreshUserStatus() {
void User::slotRefreshUserStatus()
{
// TODO: check for _account->account()->capabilities().userStatus()
if (_account.data() && _account.data()->isConnected()) {
_account.data()->fetchUserStatus();
Expand Down Expand Up @@ -698,7 +699,7 @@ Q_INVOKABLE bool UserModel::isUserConnected(const int &id)
return _users[id]->isConnected();
}

Q_INVOKABLE QUrl UserModel::statusIcon(const int &id)
Q_INVOKABLE QUrl UserModel::statusIcon(int id)
{
if (id < 0 || id >= _users.size()) {
return {};
Expand Down
2 changes: 1 addition & 1 deletion src/gui/tray/UserModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ class UserModel : public QAbstractListModel
Q_INVOKABLE bool currentUserHasLocalFolder();
int currentUserId() const;
Q_INVOKABLE bool isUserConnected(const int &id);
Q_INVOKABLE QUrl statusIcon(const int &id);
Q_INVOKABLE QUrl statusIcon(int id);
Q_INVOKABLE void switchCurrentUser(const int &id);
Q_INVOKABLE void login(const int &id);
Q_INVOKABLE void logout(const int &id);
Expand Down
93 changes: 42 additions & 51 deletions src/gui/tray/Window.qml
Original file line number Diff line number Diff line change
Expand Up @@ -403,67 +403,57 @@ Window {
}
}

// Filler between account dropdown and header app buttons
Item {
id: trayWindowHeaderSpacer
Layout.fillWidth: true
}

HeaderButton {
id: openLocalFolderButton

visible: UserModel.currentUser.hasLocalFolder
icon.source: "qrc:///client/theme/white/folder.svg"
onClicked: UserModel.openCurrentAccountLocalFolder()
RowLayout {
id: openLocalFolderRowLayout
spacing: 0
Layout.preferredWidth: Style.trayWindowHeaderHeight
Layout.preferredHeight: Style.trayWindowHeaderHeight

HeaderButton {
id: openLocalFolderButton
visible: UserModel.currentUser.hasLocalFolder
icon.source: "qrc:///client/theme/white/folder.svg"
onClicked: UserModel.openCurrentAccountLocalFolder()

Rectangle {
id: folderStateIndicatorBackground
width: Style.folderStateIndicatorSize
height: width
anchors.top: openLocalFolderButton.verticalCenter
anchors.left: openLocalFolderButton.horizontalCenter
color: Style.ncBlue
radius: width*0.5
z: 1
}
}

Image {
id: folderStateIndicator
source: UserModel.isUserConnected(UserModel.currentUserId)
? Style.stateOnlineImageSource
: Style.stateOfflineImageSource
cache: false
anchors.top: openLocalFolderButton.verticalCenter
anchors.left: openLocalFolderButton.horizontalCenter

sourceSize.width: Style.folderStateIndicatorSize
sourceSize.height: Style.folderStateIndicatorSize

Accessible.role: Accessible.Indicator
Accessible.name: UserModel.isUserConnected(UserModel.currentUserId()) ? qsTr("Connected") : qsTr("Disconnected")
}

Accessible.role: Accessible.Button
Accessible.name: qsTr("Open local folder of current account")
Accessible.onPressAction: openLocalFolderButton.clicked()
}

Rectangle {
id: folderStateIndicatorBackground
width: Style.folderStateIndicatorSize
height: width
anchors.top: openLocalFolderButton.verticalCenter
anchors.left: openLocalFolderButton.horizontalCenter
color: Style.ncBlue
radius: width*0.5
}

Rectangle {
id: folderStateRectangle
width: Style.folderStateIndicatorSize
height: width
anchors.bottom: openLocalFolderButton.bottom
anchors.right: openLocalFolderButton.right
color: openLocalFolderButton.containsMouse ? "white" : "transparent"
opacity: 0.2
radius: width*0.5
}

Image {
id: folderStateIndicator
source: UserModel.isUserConnected(UserModel.currentUserId)
? Style.stateOnlineImageSource
: Style.stateOfflineImageSource
cache: false
x: folderStateIndicatorBackground.x
y: folderStateIndicatorBackground.y
sourceSize.width: Style.folderStateIndicatorSize
sourceSize.height: Style.folderStateIndicatorSize

Accessible.role: Accessible.Indicator
Accessible.name: UserModel.isUserConnected(UserModel.currentUserId()) ? qsTr("Connected") : qsTr("Disconnected")
}

HeaderButton {
id: trayWindowTalkButton

visible: UserModel.currentUser.serverHasTalk
icon.source: "qrc:///client/theme/white/talk-app.svg"
onClicked: UserModel.openCurrentAccountTalk()

Accessible.role: Accessible.Button
Accessible.name: qsTr("Open Nextcloud Talk in browser")
Accessible.onPressAction: trayWindowTalkButton.clicked()
Expand All @@ -472,6 +462,7 @@ Window {
HeaderButton {
id: trayWindowAppsButton
icon.source: "qrc:///client/theme/white/more-apps.svg"

onClicked: {
if(appsMenu.count <= 0) {
UserModel.openCurrentAccountServer()
Expand Down
56 changes: 34 additions & 22 deletions src/gui/userstatus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,37 @@ Q_LOGGING_CATEGORY(lcUserStatus, "nextcloud.gui.userstatus", QtInfoMsg)

UserStatus::UserStatus(QObject *parent)
: QObject(parent)
, _message("")
{
}

UserStatus::Status UserStatus::stringToEnum(const QString &status) const
{
// it needs to match the Status enum
const QHash<QString, Status> preDefinedStatus{{"online", Status::Online},
{"dnd", Status::DoNotDisturb}, //DoNotDisturb
{"away", Status::Away},
{"offline", Status::Offline},
{"invisible", Status::Invisible}};

// api should return invisible, dnd,... toLower() it is to make sure
// it matches _preDefinedStatus, otherwise the default is online (0)
const auto statusEnum = _preDefinedStatus.value(status.isEmpty()? "online" : status.toLower(), 0);
return static_cast<Status>(statusEnum);
const auto statusKey = status.isEmpty() ? QStringLiteral("online") : status.toLower();
return preDefinedStatus.value(statusKey, Status::Online);
}

QString UserStatus::enumToString(Status status) const
{
switch (status) {
case Status::Away:
return tr("Away");
case Status::DoNotDisturb:
return tr("Do not disturb");
case Status::Invisible:
case Status::Offline:
return tr("Offline");
default:
return tr("Online");
}
}

void UserStatus::fetchUserStatus(AccountPtr account)
Expand All @@ -53,10 +74,9 @@ void UserStatus::fetchUserStatus(AccountPtr account)
_job->start();
}

void UserStatus::slotFetchUserStatusFinished(const QJsonDocument &json, const int statusCode)
void UserStatus::slotFetchUserStatusFinished(const QJsonDocument &json, int statusCode)
{
const QJsonObject defaultValues
{
const QJsonObject defaultValues {
{"icon", ""},
{"message", ""},
{"status", "online"}
Expand All @@ -66,19 +86,13 @@ void UserStatus::slotFetchUserStatusFinished(const QJsonDocument &json, const in
qCInfo(lcUserStatus) << "Slot fetch UserStatus finished with status code" << statusCode;
qCInfo(lcUserStatus) << "Using then default values as if user has not set any status" << defaultValues;
}

const auto retrievedData = json.object().value("ocs").toObject().value("data").toObject(defaultValues);
const auto emoji = retrievedData.value("icon").toString();
const auto message = retrievedData.value("message").toString();
auto statusString = retrievedData.value("status").toString();
_status = stringToEnum(statusString);

// to display it to the user like 'Invisible' instead of 'invisible'
statusString.replace(0, 1, statusString.at(0).toUpper());

const auto visibleStatusText = message.isEmpty()
? _status == DoNotDisturb? tr("Do not disturb")
: tr(qPrintable(statusString))
: message;
_status = stringToEnum(retrievedData.value("status").toString());
const auto visibleStatusText = message.isEmpty() ? enumToString(_status) : message;

_message = QString("%1 %2").arg(emoji, visibleStatusText);
emit fetchUserStatusFinished();
Expand All @@ -91,20 +105,18 @@ UserStatus::Status UserStatus::status() const

QString UserStatus::message() const
{
return _message;
return _message.trimmed();
}

QUrl UserStatus::icon() const
{
switch (_status) {
case Online:
return Theme::instance()->statusOnlineImageSource();
case Away:
case Status::Away:
return Theme::instance()->statusAwayImageSource();
case DoNotDisturb:
case Status::DoNotDisturb:
return Theme::instance()->statusDoNotDisturbImageSource();
case Invisible:
case Offline:
case Status::Invisible:
case Status::Offline:
return Theme::instance()->statusInvisibleImageSource();
default:
return Theme::instance()->statusOnlineImageSource();
Expand Down
15 changes: 4 additions & 11 deletions src/gui/userstatus.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class UserStatus : public QObject

public:
explicit UserStatus(QObject *parent = nullptr);
enum Status {
enum class Status {
Online,
DoNotDisturb,
Away,
Expand All @@ -42,23 +42,16 @@ class UserStatus : public QObject
QUrl icon() const;

private slots:
void slotFetchUserStatusFinished(const QJsonDocument &json, const int statusCode);
void slotFetchUserStatusFinished(const QJsonDocument &json, int statusCode);

signals:
void fetchUserStatusFinished();

private:
Status stringToEnum(const QString &status) const;

// it needs to match the Status enum
const QHash<QString, int> _preDefinedStatus{{"online", 0},
{"dnd", 1}, //DoNotDisturb
{"away", 2},
{"offline", 3},
{"invisible", 4}};

QString enumToString(Status status) const;
QPointer<JsonApiJob> _job; // the currently running job
Status _status{Status::Online};
Status _status = Status::Online;
QString _message;
};

Expand Down
2 changes: 1 addition & 1 deletion src/libsync/networkjobs.h
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ public slots:
* @brief desktopNotificationStatusReceived - signal to report if notifications are allowed
* @param status - set desktop notifications allowed status
*/
void allowDesktopNotificationsChanged(const bool isAllowed);
void allowDesktopNotificationsChanged(bool isAllowed);

private:
QUrlQuery _additionalParams;
Expand Down

0 comments on commit f6afb62

Please sign in to comment.