diff options
author | Felix Weilbach <felix.weilbach@nextcloud.com> | 2021-09-09 12:18:22 +0300 |
---|---|---|
committer | Felix Weilbach <felix.weilbach@nextcloud.com> | 2021-09-09 12:18:22 +0300 |
commit | 8a8d488454405356b5d11f63bebff2d69be43b02 (patch) | |
tree | afa6c5a496561e6778227ece22a6c871622af926 /src/gui/userstatusselectormodel.cpp | |
parent | f34d66302942ede371bf7bc5d5e213b6c41ea5d8 (diff) |
Add dialog to set user status
Signed-off-by: Felix Weilbach <felix.weilbach@nextcloud.com>
Diffstat (limited to 'src/gui/userstatusselectormodel.cpp')
-rw-r--r-- | src/gui/userstatusselectormodel.cpp | 474 |
1 files changed, 474 insertions, 0 deletions
diff --git a/src/gui/userstatusselectormodel.cpp b/src/gui/userstatusselectormodel.cpp new file mode 100644 index 000000000..c241474e0 --- /dev/null +++ b/src/gui/userstatusselectormodel.cpp @@ -0,0 +1,474 @@ +/* + * Copyright (C) by Felix Weilbach <felix.weilbach@nextcloud.com> + * + * 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. + */ + +#include "userstatusselectormodel.h" +#include "tray/UserModel.h" + +#include <ocsuserstatusconnector.h> +#include <qnamespace.h> +#include <userstatusconnector.h> +#include <theme.h> + +#include <QDateTime> +#include <QLoggingCategory> + +#include <algorithm> +#include <cmath> +#include <cstddef> + +namespace OCC { + +Q_LOGGING_CATEGORY(lcUserStatusDialogModel, "nextcloud.gui.userstatusdialogmodel", QtInfoMsg) + +UserStatusSelectorModel::UserStatusSelectorModel(QObject *parent) + : QObject(parent) + , _dateTimeProvider(new DateTimeProvider) +{ + _userStatus.setIcon("😀"); +} + +UserStatusSelectorModel::UserStatusSelectorModel(std::shared_ptr<UserStatusConnector> userStatusConnector, QObject *parent) + : QObject(parent) + , _userStatusConnector(userStatusConnector) + , _userStatus("no-id", "", "😀", UserStatus::OnlineStatus::Online, false, {}) + , _dateTimeProvider(new DateTimeProvider) +{ + _userStatus.setIcon("😀"); + init(); +} + +UserStatusSelectorModel::UserStatusSelectorModel(std::shared_ptr<UserStatusConnector> userStatusConnector, + std::unique_ptr<DateTimeProvider> dateTimeProvider, + QObject *parent) + : QObject(parent) + , _userStatusConnector(userStatusConnector) + , _dateTimeProvider(std::move(dateTimeProvider)) +{ + _userStatus.setIcon("😀"); + init(); +} + +UserStatusSelectorModel::UserStatusSelectorModel(const UserStatus &userStatus, + std::unique_ptr<DateTimeProvider> dateTimeProvider, QObject *parent) + : QObject(parent) + , _userStatus(userStatus) + , _dateTimeProvider(std::move(dateTimeProvider)) +{ + _userStatus.setIcon("😀"); +} + +UserStatusSelectorModel::UserStatusSelectorModel(const UserStatus &userStatus, + QObject *parent) + : QObject(parent) + , _userStatus(userStatus) +{ + _userStatus.setIcon("😀"); +} + +void UserStatusSelectorModel::load(int id) +{ + reset(); + _userStatusConnector = UserModel::instance()->userStatusConnector(id); + init(); +} + +void UserStatusSelectorModel::reset() +{ + if (_userStatusConnector) { + disconnect(_userStatusConnector.get(), &UserStatusConnector::userStatusFetched, this, + &UserStatusSelectorModel::onUserStatusFetched); + disconnect(_userStatusConnector.get(), &UserStatusConnector::predefinedStatusesFetched, this, + &UserStatusSelectorModel::onPredefinedStatusesFetched); + disconnect(_userStatusConnector.get(), &UserStatusConnector::error, this, + &UserStatusSelectorModel::onError); + disconnect(_userStatusConnector.get(), &UserStatusConnector::userStatusSet, this, + &UserStatusSelectorModel::onUserStatusSet); + disconnect(_userStatusConnector.get(), &UserStatusConnector::messageCleared, this, + &UserStatusSelectorModel::onMessageCleared); + } + _userStatusConnector = nullptr; +} + +void UserStatusSelectorModel::init() +{ + if (!_userStatusConnector) { + return; + } + + connect(_userStatusConnector.get(), &UserStatusConnector::userStatusFetched, this, + &UserStatusSelectorModel::onUserStatusFetched); + connect(_userStatusConnector.get(), &UserStatusConnector::predefinedStatusesFetched, this, + &UserStatusSelectorModel::onPredefinedStatusesFetched); + connect(_userStatusConnector.get(), &UserStatusConnector::error, this, + &UserStatusSelectorModel::onError); + connect(_userStatusConnector.get(), &UserStatusConnector::userStatusSet, this, + &UserStatusSelectorModel::onUserStatusSet); + connect(_userStatusConnector.get(), &UserStatusConnector::messageCleared, this, + &UserStatusSelectorModel::onMessageCleared); + + _userStatusConnector->fetchUserStatus(); + _userStatusConnector->fetchPredefinedStatuses(); +} + +UserStatusSelectorModel::~UserStatusSelectorModel() +{ + qCDebug(lcUserStatusDialogModel) << "Destroyed"; +} + +void UserStatusSelectorModel::onUserStatusSet() +{ + qCDebug(lcUserStatusDialogModel) << "Emit finished"; + emit finished(); +} + +void UserStatusSelectorModel::onMessageCleared() +{ + emit finished(); +} + +void UserStatusSelectorModel::onError(UserStatusConnector::Error error) +{ + qCWarning(lcUserStatusDialogModel) << "Error:" << error; + + switch (error) { + case UserStatusConnector::Error::CouldNotFetchPredefinedUserStatuses: + setError(tr("Could not fetch predefined statuses. Make sure you are connected to the server.")); + return; + + case UserStatusConnector::Error::CouldNotFetchUserStatus: + setError(tr("Could not fetch user status. Make sure you are connected to the server.")); + return; + + case UserStatusConnector::Error::UserStatusNotSupported: + setError(tr("User status feature is not supported. You will not be able to set your user status.")); + return; + + case UserStatusConnector::Error::EmojisNotSupported: + setError(tr("Emojis feature is not supported. Some user status functionality may not work.")); + return; + + case UserStatusConnector::Error::CouldNotSetUserStatus: + setError(tr("Could not set user status. Make sure you are connected to the server.")); + return; + + case UserStatusConnector::Error::CouldNotClearMessage: + setError(tr("Could not clear user status message. Make sure you are connected to the server.")); + return; + } + + Q_UNREACHABLE(); +} + +void UserStatusSelectorModel::setError(const QString &reason) +{ + _errorMessage = reason; + emit errorMessageChanged(); +} + +void UserStatusSelectorModel::clearError() +{ + setError(""); +} + +void UserStatusSelectorModel::setOnlineStatus(UserStatus::OnlineStatus status) +{ + if (status == _userStatus.state()) { + return; + } + + _userStatus.setState(status); + emit onlineStatusChanged(); +} + +QUrl UserStatusSelectorModel::onlineIcon() const +{ + return Theme::instance()->statusOnlineImageSource(); +} + +QUrl UserStatusSelectorModel::awayIcon() const +{ + return Theme::instance()->statusAwayImageSource(); +} +QUrl UserStatusSelectorModel::dndIcon() const +{ + return Theme::instance()->statusDoNotDisturbImageSource(); +} +QUrl UserStatusSelectorModel::invisibleIcon() const +{ + return Theme::instance()->statusInvisibleImageSource(); +} + +UserStatus::OnlineStatus UserStatusSelectorModel::onlineStatus() const +{ + return _userStatus.state(); +} + +QString UserStatusSelectorModel::userStatusMessage() const +{ + return _userStatus.message(); +} + +void UserStatusSelectorModel::setUserStatusMessage(const QString &message) +{ + _userStatus.setMessage(message); + _userStatus.setMessagePredefined(false); + emit userStatusChanged(); +} + +void UserStatusSelectorModel::setUserStatusEmoji(const QString &emoji) +{ + _userStatus.setIcon(emoji); + _userStatus.setMessagePredefined(false); + emit userStatusChanged(); +} + +QString UserStatusSelectorModel::userStatusEmoji() const +{ + return _userStatus.icon(); +} + +void UserStatusSelectorModel::onUserStatusFetched(const UserStatus &userStatus) +{ + if (userStatus.state() != UserStatus::OnlineStatus::Offline) { + _userStatus.setState(userStatus.state()); + } + _userStatus.setMessage(userStatus.message()); + _userStatus.setMessagePredefined(userStatus.messagePredefined()); + _userStatus.setId(userStatus.id()); + _userStatus.setClearAt(userStatus.clearAt()); + + if (!userStatus.icon().isEmpty()) { + _userStatus.setIcon(userStatus.icon()); + } + + emit userStatusChanged(); + emit onlineStatusChanged(); + emit clearAtChanged(); +} + +Optional<ClearAt> UserStatusSelectorModel::clearStageTypeToDateTime(ClearStageType type) const +{ + switch (type) { + case ClearStageType::DontClear: + return {}; + + case ClearStageType::HalfHour: { + ClearAt clearAt; + clearAt._type = ClearAtType::Period; + clearAt._period = 60 * 30; + return clearAt; + } + + case ClearStageType::OneHour: { + ClearAt clearAt; + clearAt._type = ClearAtType::Period; + clearAt._period = 60 * 60; + return clearAt; + } + + case ClearStageType::FourHour: { + ClearAt clearAt; + clearAt._type = ClearAtType::Period; + clearAt._period = 60 * 60 * 4; + return clearAt; + } + + case ClearStageType::Today: { + ClearAt clearAt; + clearAt._type = ClearAtType::EndOf; + clearAt._endof = "day"; + return clearAt; + } + + case ClearStageType::Week: { + ClearAt clearAt; + clearAt._type = ClearAtType::EndOf; + clearAt._endof = "week"; + return clearAt; + } + + default: + Q_UNREACHABLE(); + } +} + +void UserStatusSelectorModel::setUserStatus() +{ + Q_ASSERT(_userStatusConnector); + if (!_userStatusConnector) { + return; + } + + clearError(); + _userStatusConnector->setUserStatus(_userStatus); +} + +void UserStatusSelectorModel::clearUserStatus() +{ + Q_ASSERT(_userStatusConnector); + if (!_userStatusConnector) { + return; + } + + clearError(); + _userStatusConnector->clearMessage(); +} + +void UserStatusSelectorModel::onPredefinedStatusesFetched(const std::vector<UserStatus> &statuses) +{ + _predefinedStatuses = statuses; + emit predefinedStatusesChanged(); +} + +UserStatus UserStatusSelectorModel::predefinedStatus(int index) const +{ + Q_ASSERT(0 <= index && index < static_cast<int>(_predefinedStatuses.size())); + return _predefinedStatuses[index]; +} + +int UserStatusSelectorModel::predefinedStatusesCount() const +{ + return static_cast<int>(_predefinedStatuses.size()); +} + +void UserStatusSelectorModel::setPredefinedStatus(int index) +{ + Q_ASSERT(0 <= index && index < static_cast<int>(_predefinedStatuses.size())); + + _userStatus.setMessagePredefined(true); + const auto predefinedStatus = _predefinedStatuses[index]; + _userStatus.setId(predefinedStatus.id()); + _userStatus.setMessage(predefinedStatus.message()); + _userStatus.setIcon(predefinedStatus.icon()); + _userStatus.setClearAt(predefinedStatus.clearAt()); + + emit userStatusChanged(); + emit clearAtChanged(); +} + +QString UserStatusSelectorModel::clearAtStageToString(ClearStageType stage) const +{ + switch (stage) { + case ClearStageType::DontClear: + return tr("Don't clear"); + + case ClearStageType::HalfHour: + return tr("30 minutes"); + + case ClearStageType::OneHour: + return tr("1 hour"); + + case ClearStageType::FourHour: + return tr("4 hours"); + + case ClearStageType::Today: + return tr("Today"); + + case ClearStageType::Week: + return tr("This week"); + + default: + Q_UNREACHABLE(); + } +} + +QStringList UserStatusSelectorModel::clearAtValues() const +{ + QStringList clearAtStages; + std::transform(_clearStages.begin(), _clearStages.end(), + std::back_inserter(clearAtStages), + [this](const ClearStageType &stage) { return clearAtStageToString(stage); }); + + return clearAtStages; +} + +void UserStatusSelectorModel::setClearAt(int index) +{ + Q_ASSERT(0 <= index && index < static_cast<int>(_clearStages.size())); + _userStatus.setClearAt(clearStageTypeToDateTime(_clearStages[index])); + emit clearAtChanged(); +} + +QString UserStatusSelectorModel::errorMessage() const +{ + return _errorMessage; +} + +QString UserStatusSelectorModel::timeDifferenceToString(int differenceSecs) const +{ + if (differenceSecs < 60) { + return tr("Less than a minute"); + } else if (differenceSecs < 60 * 60) { + const auto minutesLeft = std::ceil(differenceSecs / 60.0); + if (minutesLeft == 1) { + return tr("1 minute"); + } else { + return tr("%1 minutes").arg(minutesLeft); + } + } else if (differenceSecs < 60 * 60 * 24) { + const auto hoursLeft = std::ceil(differenceSecs / 60.0 / 60.0); + if (hoursLeft == 1) { + return tr("1 hour"); + } else { + return tr("%1 hours").arg(hoursLeft); + } + } else { + const auto daysLeft = std::ceil(differenceSecs / 60.0 / 60.0 / 24.0); + if (daysLeft == 1) { + return tr("1 day"); + } else { + return tr("%1 days").arg(daysLeft); + } + } +} + +QString UserStatusSelectorModel::clearAtReadable(const Optional<ClearAt> &clearAt) const +{ + if (clearAt) { + switch (clearAt->_type) { + case ClearAtType::Period: { + return timeDifferenceToString(clearAt->_period); + } + + case ClearAtType::Timestamp: { + const int difference = static_cast<int>(clearAt->_timestamp - _dateTimeProvider->currentDateTime().toTime_t()); + return timeDifferenceToString(difference); + } + + case ClearAtType::EndOf: { + if (clearAt->_endof == "day") { + return tr("Today"); + } else if (clearAt->_endof == "week") { + return tr("This week"); + } + Q_UNREACHABLE(); + } + + default: + Q_UNREACHABLE(); + } + } + return tr("Don't clear"); +} + +QString UserStatusSelectorModel::predefinedStatusClearAt(int index) const +{ + return clearAtReadable(predefinedStatus(index).clearAt()); +} + +QString UserStatusSelectorModel::clearAt() const +{ + return clearAtReadable(_userStatus.clearAt()); +} +} |