From b7bf6083d90263a674ccca29e80b81ebe8b62c0e Mon Sep 17 00:00:00 2001 From: Camila Ayres Date: Tue, 16 Apr 2024 18:24:11 +0200 Subject: [PATCH 1/2] Display the Nextcloud assistant icon in the main window. - If NC assistant is enabled in the server, display the icon to open it instead of Talk. Talk is then displayed in the list with the other apps. - The direct link is only available in the assistant app >= 1.0.9. Signed-off-by: Camila Ayres --- src/gui/tray/Window.qml | 16 ++++++++-------- src/gui/tray/usermodel.cpp | 25 +++++++++++++++++++++++-- src/gui/tray/usermodel.h | 4 ++++ src/libsync/capabilities.cpp | 21 ++++++++++++++++++++- src/libsync/capabilities.h | 1 + theme.qrc.in | 1 + theme/white/nc-assistant-app.svg | 3 +++ 7 files changed, 60 insertions(+), 11 deletions(-) create mode 100644 theme/white/nc-assistant-app.svg diff --git a/src/gui/tray/Window.qml b/src/gui/tray/Window.qml index dd21ccccaf6f5..00c6f887e701d 100644 --- a/src/gui/tray/Window.qml +++ b/src/gui/tray/Window.qml @@ -621,21 +621,21 @@ ApplicationWindow { } HeaderButton { - id: trayWindowTalkButton - - visible: UserModel.currentUser && UserModel.currentUser.serverHasTalk - icon.source: "image://svgimage-custom-color/talk-app.svg" + "/" + Style.currentUserHeaderTextColor + id: trayWindowFeaturedAppButton + visible: UserModel.currentUser.isNcAssistantEnabled || UserModel.currentUser.serverHasTalk + icon.source: UserModel.currentUser.isNcAssistantEnabled + ? "image:///client/theme/white/nc-assistant-app.svg" + "/" + Style.currentUserHeaderTextColor + : "image:///client/theme/white/talk-app.svg" + "/" + Style.currentUserHeaderTextColor icon.color: Style.currentUserHeaderTextColor - onClicked: UserModel.openCurrentAccountTalk() + onClicked: UserModel.currentUser.isNcAssistantEnabled ? UserModel.openCurrentAccountNcAssistant() : UserModel.openCurrentAccountTalk() Accessible.role: Accessible.Button - Accessible.name: qsTr("Open Nextcloud Talk in browser") - Accessible.onPressAction: trayWindowTalkButton.clicked() + Accessible.name: UserModel.currentUser.isNcAssistantEnabled ? qsTr("Open Nextcloud Assistant in browser") : qsTr("Open Nextcloud Talk in browser") + Accessible.onPressAction: trayWindowFeaturedAppButton.clicked() Layout.alignment: Qt.AlignRight Layout.preferredWidth: Style.trayWindowHeaderHeight Layout.preferredHeight: Style.trayWindowHeaderHeight - } HeaderButton { diff --git a/src/gui/tray/usermodel.cpp b/src/gui/tray/usermodel.cpp index 7cb3b08e7afa7..a694ac8d950c9 100644 --- a/src/gui/tray/usermodel.cpp +++ b/src/gui/tray/usermodel.cpp @@ -471,6 +471,7 @@ void User::slotRefreshNotifications() void User::slotRebuildNavigationAppList() { emit serverHasTalkChanged(); + emit ncAssistantAvailabityChanged(); // Rebuild App list UserAppsModel::instance()->buildAppList(); } @@ -1046,6 +1047,11 @@ bool User::hasActivities() const return _account->account()->capabilities().hasActivities(); } +bool User::isNcAssistantEnabled() const +{ + return _account->account()->capabilities().ncAssistantEnabled(); +} + QColor User::headerColor() const { return _account->account()->headerColor(); @@ -1364,6 +1370,20 @@ void UserModel::openCurrentAccountFolderFromTrayInfo(const QString &fullRemotePa _users[_currentUserId]->openFolderLocallyOrInBrowser(fullRemotePath); } +void UserModel::openCurrentAccountNcAssistant() +{ + if (!currentUser()) { + return; + } + + if (currentUser()->isNcAssistantEnabled()) { + QDesktopServices::openUrl(QUrl(_users[_currentUserId]->server(false).append("/apps/assistant/"))); + } else { + qCWarning(lcActivity) << "The Nextcloud Assistant app is not enabled on" << currentUser()->server(); + } +} + + void UserModel::setCurrentUserId(const int id) { Q_ASSERT(id < _users.size()); @@ -1630,10 +1650,11 @@ void UserAppsModel::buildAppList() if (UserModel::instance()->appList().count() > 0) { const auto talkApp = UserModel::instance()->currentUser()->talkApp(); - foreach (AccountApp *app, UserModel::instance()->appList()) { + for (auto &app : UserModel::instance()->appList()) { // Filter out Talk because we have a dedicated button for it - if (talkApp && app->id() == talkApp->id()) + if (talkApp && app->id() == talkApp->id() && !UserModel::instance()->currentUser()->isNcAssistantEnabled()) { continue; + } beginInsertRows(QModelIndex(), rowCount(), rowCount()); _apps << app; diff --git a/src/gui/tray/usermodel.h b/src/gui/tray/usermodel.h index 37f039f3a79be..f2949952038d9 100644 --- a/src/gui/tray/usermodel.h +++ b/src/gui/tray/usermodel.h @@ -57,6 +57,7 @@ class User : public QObject Q_PROPERTY(bool desktopNotificationsAllowed READ isDesktopNotificationsAllowed NOTIFY desktopNotificationsAllowedChanged) Q_PROPERTY(bool hasLocalFolder READ hasLocalFolder NOTIFY hasLocalFolderChanged) Q_PROPERTY(bool serverHasTalk READ serverHasTalk NOTIFY serverHasTalkChanged) + Q_PROPERTY(bool isNcAssistantEnabled READ isNcAssistantEnabled NOTIFY ncAssistantAvailabityChanged) Q_PROPERTY(QString avatar READ avatarUrl NOTIFY avatarChanged) Q_PROPERTY(bool isConnected READ isConnected NOTIFY accountStateChanged) Q_PROPERTY(UnifiedSearchResultsListModel* unifiedSearchResultsListModel READ getUnifiedSearchResultsListModel CONSTANT) @@ -83,6 +84,7 @@ class User : public QObject [[nodiscard]] bool serverHasUserStatus() const; [[nodiscard]] AccountApp *talkApp() const; [[nodiscard]] bool hasActivities() const; + [[nodiscard]] bool isNcAssistantEnabled() const; [[nodiscard]] QColor accentColor() const; [[nodiscard]] QColor headerColor() const; [[nodiscard]] QColor headerTextColor() const; @@ -113,6 +115,7 @@ class User : public QObject void accentColorChanged(); void sendReplyMessage(const int activityIndex, const QString &conversationToken, const QString &message, const QString &replyTo); void groupFoldersChanged(); + void ncAssistantAvailabityChanged(); public slots: void slotItemCompleted(const QString &folder, const OCC::SyncFileItemPtr &item); @@ -251,6 +254,7 @@ public slots: void openCurrentAccountTalk(); void openCurrentAccountServer(); void openCurrentAccountFolderFromTrayInfo(const QString &fullRemotePath); + void openCurrentAccountNcAssistant(); void setCurrentUserId(const int id); void login(const int id); void logout(const int id); diff --git a/src/libsync/capabilities.cpp b/src/libsync/capabilities.cpp index a189da2ac3fba..ee9ff332cc90f 100644 --- a/src/libsync/capabilities.cpp +++ b/src/libsync/capabilities.cpp @@ -17,7 +17,7 @@ #include #include #include - +#include #include namespace OCC { @@ -277,6 +277,25 @@ bool Capabilities::userStatusSupportsEmoji() const return userStatusMap.value("supports_emoji", false).toBool(); } +bool Capabilities::ncAssistantEnabled() const +{ + if (_capabilities.contains("assistant") + && _capabilities["assistant"].toMap()["enabled"].toBool()) { + + const auto minimumVersion = QVersionNumber(1, 0, 9); + const auto versionString = _capabilities["assistant"].toMap()["version"].toString(); + + if (const auto currentVersion = QVersionNumber::fromString(versionString); + QVersionNumber::compare(currentVersion, minimumVersion) >= 0) { + return true; + } + + qCInfo(lcServerCapabilities) << "The NC Assistant app only provides a direct link starting at 1.0.9."; + } + + return false; +} + QColor Capabilities::serverColor() const { const auto themingMap = serverThemingMap(); diff --git a/src/libsync/capabilities.h b/src/libsync/capabilities.h index 1dd0514a28f69..110b507751600 100644 --- a/src/libsync/capabilities.h +++ b/src/libsync/capabilities.h @@ -69,6 +69,7 @@ class OWNCLOUDSYNC_EXPORT Capabilities [[nodiscard]] bool filesLockTypeAvailable() const; [[nodiscard]] bool userStatus() const; [[nodiscard]] bool userStatusSupportsEmoji() const; + [[nodiscard]] bool ncAssistantEnabled() const; [[nodiscard]] QColor serverColor() const; [[nodiscard]] QColor serverTextColor() const; diff --git a/theme.qrc.in b/theme.qrc.in index 2405e4335afe6..137b3c90ccf37 100644 --- a/theme.qrc.in +++ b/theme.qrc.in @@ -189,6 +189,7 @@ theme/white/folder.svg theme/white/more-apps.svg theme/white/talk-app.svg + theme/white/nc-assistant-app.svg theme/white/caret-down.svg theme/black/caret-down.svg theme/white/user.svg diff --git a/theme/white/nc-assistant-app.svg b/theme/white/nc-assistant-app.svg new file mode 100644 index 0000000000000..a0233786fb757 --- /dev/null +++ b/theme/white/nc-assistant-app.svg @@ -0,0 +1,3 @@ + + + From fcf07b60c4ea4ea9f7d3c2431d159b75686f81ae Mon Sep 17 00:00:00 2001 From: Camila Ayres Date: Tue, 4 Jun 2024 14:47:44 +0200 Subject: [PATCH 2/2] Move the logic for handling the featured app qml to cpp. Signed-off-by: Camila Ayres --- src/gui/tray/Window.qml | 11 +++----- src/gui/tray/usermodel.cpp | 52 +++++++++++++++++++++++--------------- src/gui/tray/usermodel.h | 17 ++++++++----- 3 files changed, 46 insertions(+), 34 deletions(-) diff --git a/src/gui/tray/Window.qml b/src/gui/tray/Window.qml index 00c6f887e701d..386c8990b2776 100644 --- a/src/gui/tray/Window.qml +++ b/src/gui/tray/Window.qml @@ -622,15 +622,12 @@ ApplicationWindow { HeaderButton { id: trayWindowFeaturedAppButton - visible: UserModel.currentUser.isNcAssistantEnabled || UserModel.currentUser.serverHasTalk - icon.source: UserModel.currentUser.isNcAssistantEnabled - ? "image:///client/theme/white/nc-assistant-app.svg" + "/" + Style.currentUserHeaderTextColor - : "image:///client/theme/white/talk-app.svg" + "/" + Style.currentUserHeaderTextColor - icon.color: Style.currentUserHeaderTextColor - onClicked: UserModel.currentUser.isNcAssistantEnabled ? UserModel.openCurrentAccountNcAssistant() : UserModel.openCurrentAccountTalk() + visible: UserModel.currentUser.isFeaturedAppEnabled + icon.source: UserModel.currentUser.featuredAppIcon + "/" + Style.currentUserHeaderTextColor + onClicked: UserModel.openCurrentAccountFeaturedApp() Accessible.role: Accessible.Button - Accessible.name: UserModel.currentUser.isNcAssistantEnabled ? qsTr("Open Nextcloud Assistant in browser") : qsTr("Open Nextcloud Talk in browser") + Accessible.name: UserModel.currentUser.featuredAppAccessibleName Accessible.onPressAction: trayWindowFeaturedAppButton.clicked() Layout.alignment: Qt.AlignRight diff --git a/src/gui/tray/usermodel.cpp b/src/gui/tray/usermodel.cpp index a694ac8d950c9..908f760bdd896 100644 --- a/src/gui/tray/usermodel.cpp +++ b/src/gui/tray/usermodel.cpp @@ -470,8 +470,7 @@ void User::slotRefreshNotifications() void User::slotRebuildNavigationAppList() { - emit serverHasTalkChanged(); - emit ncAssistantAvailabityChanged(); + emit featuredAppChanged(); // Rebuild App list UserAppsModel::instance()->buildAppList(); } @@ -1037,6 +1036,22 @@ bool User::serverHasTalk() const return talkApp() != nullptr; } +bool User::isFeaturedAppEnabled() const +{ + return isNcAssistantEnabled() || serverHasTalk(); +} + +QString User::featuredAppIcon() const +{ + return isNcAssistantEnabled() ? "image://svgimage-custom-color/nc-assistant-app.svg" + : "image://svgimage-custom-color/talk-app.svg"; +} + +QString User::featuredAppAccessibleName() const +{ + return isNcAssistantEnabled() ? tr("Open Nextcloud Assistant in browser") : tr("Open Nextcloud Talk in browser"); +} + AccountApp *User::talkApp() const { return _account->findApp(QStringLiteral("spreed")); @@ -1335,19 +1350,6 @@ void UserModel::openCurrentAccountLocalFolder() _users[_currentUserId]->openLocalFolder(); } -void UserModel::openCurrentAccountTalk() -{ - if (!currentUser()) - return; - - const auto talkApp = currentUser()->talkApp(); - if (talkApp) { - Utility::openBrowser(talkApp->url()); - } else { - qCWarning(lcActivity) << "The Talk app is not enabled on" << currentUser()->server(); - } -} - void UserModel::openCurrentAccountServer() { if (_currentUserId < 0 || _currentUserId >= _users.size()) @@ -1370,16 +1372,26 @@ void UserModel::openCurrentAccountFolderFromTrayInfo(const QString &fullRemotePa _users[_currentUserId]->openFolderLocallyOrInBrowser(fullRemotePath); } -void UserModel::openCurrentAccountNcAssistant() +void UserModel::openCurrentAccountFeaturedApp() { if (!currentUser()) { return; } + if (!currentUser()->isFeaturedAppEnabled()) { + qCWarning(lcActivity) << "There is no feature app enabled on" << currentUser()->server(); + return; + } + if (currentUser()->isNcAssistantEnabled()) { - QDesktopServices::openUrl(QUrl(_users[_currentUserId]->server(false).append("/apps/assistant/"))); - } else { - qCWarning(lcActivity) << "The Nextcloud Assistant app is not enabled on" << currentUser()->server(); + auto serverUrl = currentUser()->server(false); + const auto assistanceUrl = serverUrl.append("/apps/assistant/"); + QDesktopServices::openUrl(QUrl::fromUserInput(assistanceUrl)); + return; + } + + if (const auto talkApp = currentUser()->talkApp()) { + Utility::openBrowser(talkApp->url()); } } @@ -1650,7 +1662,7 @@ void UserAppsModel::buildAppList() if (UserModel::instance()->appList().count() > 0) { const auto talkApp = UserModel::instance()->currentUser()->talkApp(); - for (auto &app : UserModel::instance()->appList()) { + for (const auto &app : UserModel::instance()->appList()) { // Filter out Talk because we have a dedicated button for it if (talkApp && app->id() == talkApp->id() && !UserModel::instance()->currentUser()->isNcAssistantEnabled()) { continue; diff --git a/src/gui/tray/usermodel.h b/src/gui/tray/usermodel.h index f2949952038d9..33c3ae455a5bb 100644 --- a/src/gui/tray/usermodel.h +++ b/src/gui/tray/usermodel.h @@ -56,8 +56,9 @@ class User : public QObject Q_PROPERTY(QString statusMessage READ statusMessage NOTIFY statusChanged) Q_PROPERTY(bool desktopNotificationsAllowed READ isDesktopNotificationsAllowed NOTIFY desktopNotificationsAllowedChanged) Q_PROPERTY(bool hasLocalFolder READ hasLocalFolder NOTIFY hasLocalFolderChanged) - Q_PROPERTY(bool serverHasTalk READ serverHasTalk NOTIFY serverHasTalkChanged) - Q_PROPERTY(bool isNcAssistantEnabled READ isNcAssistantEnabled NOTIFY ncAssistantAvailabityChanged) + Q_PROPERTY(bool isFeaturedAppEnabled READ isFeaturedAppEnabled NOTIFY featuredAppChanged) + 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(bool isConnected READ isConnected NOTIFY accountStateChanged) Q_PROPERTY(UnifiedSearchResultsListModel* unifiedSearchResultsListModel READ getUnifiedSearchResultsListModel CONSTANT) @@ -80,7 +81,9 @@ class User : public QObject [[nodiscard]] QString name() const; [[nodiscard]] QString server(bool shortened = true) const; [[nodiscard]] bool hasLocalFolder() const; - [[nodiscard]] bool serverHasTalk() const; + [[nodiscard]] bool isFeaturedAppEnabled() const; + [[nodiscard]] QString featuredAppIcon() const; + [[nodiscard]] QString featuredAppAccessibleName() const; [[nodiscard]] bool serverHasUserStatus() const; [[nodiscard]] AccountApp *talkApp() const; [[nodiscard]] bool hasActivities() const; @@ -105,7 +108,7 @@ class User : public QObject signals: void nameChanged(); void hasLocalFolderChanged(); - void serverHasTalkChanged(); + void featuredAppChanged(); void avatarChanged(); void accountStateChanged(); void statusChanged(); @@ -115,7 +118,6 @@ class User : public QObject void accentColorChanged(); void sendReplyMessage(const int activityIndex, const QString &conversationToken, const QString &message, const QString &replyTo); void groupFoldersChanged(); - void ncAssistantAvailabityChanged(); public slots: void slotItemCompleted(const QString &folder, const OCC::SyncFileItemPtr &item); @@ -171,6 +173,8 @@ private slots: void checkAndRemoveSeenActivities(const ActivityList &list, const int numTalkNotificationsReceived); + [[nodiscard]] bool serverHasTalk() const; + AccountStatePtr _account; bool _isCurrentUser; ActivityListModel *_activityModel; @@ -251,10 +255,9 @@ class UserModel : public QAbstractListModel public slots: void fetchCurrentActivityModel(); void openCurrentAccountLocalFolder(); - void openCurrentAccountTalk(); void openCurrentAccountServer(); void openCurrentAccountFolderFromTrayInfo(const QString &fullRemotePath); - void openCurrentAccountNcAssistant(); + void openCurrentAccountFeaturedApp(); void setCurrentUserId(const int id); void login(const int id); void logout(const int id);