diff --git a/src/gui/tray/Window.qml b/src/gui/tray/Window.qml index dd21ccccaf6f5..386c8990b2776 100644 --- a/src/gui/tray/Window.qml +++ b/src/gui/tray/Window.qml @@ -621,21 +621,18 @@ ApplicationWindow { } HeaderButton { - id: trayWindowTalkButton - - visible: UserModel.currentUser && UserModel.currentUser.serverHasTalk - icon.source: "image://svgimage-custom-color/talk-app.svg" + "/" + Style.currentUserHeaderTextColor - icon.color: Style.currentUserHeaderTextColor - onClicked: UserModel.openCurrentAccountTalk() + id: trayWindowFeaturedAppButton + visible: UserModel.currentUser.isFeaturedAppEnabled + icon.source: UserModel.currentUser.featuredAppIcon + "/" + Style.currentUserHeaderTextColor + onClicked: UserModel.openCurrentAccountFeaturedApp() Accessible.role: Accessible.Button - Accessible.name: qsTr("Open Nextcloud Talk in browser") - Accessible.onPressAction: trayWindowTalkButton.clicked() + Accessible.name: UserModel.currentUser.featuredAppAccessibleName + 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..908f760bdd896 100644 --- a/src/gui/tray/usermodel.cpp +++ b/src/gui/tray/usermodel.cpp @@ -470,7 +470,7 @@ void User::slotRefreshNotifications() void User::slotRebuildNavigationAppList() { - emit serverHasTalkChanged(); + emit featuredAppChanged(); // Rebuild App list UserAppsModel::instance()->buildAppList(); } @@ -1036,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")); @@ -1046,6 +1062,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(); @@ -1329,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()) @@ -1364,6 +1372,30 @@ void UserModel::openCurrentAccountFolderFromTrayInfo(const QString &fullRemotePa _users[_currentUserId]->openFolderLocallyOrInBrowser(fullRemotePath); } +void UserModel::openCurrentAccountFeaturedApp() +{ + if (!currentUser()) { + return; + } + + if (!currentUser()->isFeaturedAppEnabled()) { + qCWarning(lcActivity) << "There is no feature app enabled on" << currentUser()->server(); + return; + } + + if (currentUser()->isNcAssistantEnabled()) { + 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()); + } +} + + void UserModel::setCurrentUserId(const int id) { Q_ASSERT(id < _users.size()); @@ -1630,10 +1662,11 @@ void UserAppsModel::buildAppList() if (UserModel::instance()->appList().count() > 0) { const auto talkApp = UserModel::instance()->currentUser()->talkApp(); - foreach (AccountApp *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()) + 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..33c3ae455a5bb 100644 --- a/src/gui/tray/usermodel.h +++ b/src/gui/tray/usermodel.h @@ -56,7 +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 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) @@ -79,10 +81,13 @@ 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; + [[nodiscard]] bool isNcAssistantEnabled() const; [[nodiscard]] QColor accentColor() const; [[nodiscard]] QColor headerColor() const; [[nodiscard]] QColor headerTextColor() const; @@ -103,7 +108,7 @@ class User : public QObject signals: void nameChanged(); void hasLocalFolderChanged(); - void serverHasTalkChanged(); + void featuredAppChanged(); void avatarChanged(); void accountStateChanged(); void statusChanged(); @@ -168,6 +173,8 @@ private slots: void checkAndRemoveSeenActivities(const ActivityList &list, const int numTalkNotificationsReceived); + [[nodiscard]] bool serverHasTalk() const; + AccountStatePtr _account; bool _isCurrentUser; ActivityListModel *_activityModel; @@ -248,9 +255,9 @@ class UserModel : public QAbstractListModel public slots: void fetchCurrentActivityModel(); void openCurrentAccountLocalFolder(); - void openCurrentAccountTalk(); void openCurrentAccountServer(); void openCurrentAccountFolderFromTrayInfo(const QString &fullRemotePath); + void openCurrentAccountFeaturedApp(); 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 @@ + + +