userstatusselectormodel.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. /*
  2. * Copyright (C) by Felix Weilbach <felix.weilbach@nextcloud.com>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  11. * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  12. * for more details.
  13. */
  14. #include "userstatusselectormodel.h"
  15. #include "tray/usermodel.h"
  16. #include <qnamespace.h>
  17. #include <theme.h>
  18. #include <QDateTime>
  19. #include <QLoggingCategory>
  20. #include <algorithm>
  21. #include <cmath>
  22. #include <cstddef>
  23. namespace OCC {
  24. Q_LOGGING_CATEGORY(lcUserStatusDialogModel, "nextcloud.gui.userstatusdialogmodel", QtInfoMsg)
  25. UserStatusSelectorModel::UserStatusSelectorModel(QObject *parent)
  26. : QObject(parent)
  27. , _dateTimeProvider(new DateTimeProvider)
  28. {
  29. _userStatus.setIcon("😀");
  30. }
  31. UserStatusSelectorModel::UserStatusSelectorModel(std::shared_ptr<UserStatusConnector> userStatusConnector, QObject *parent)
  32. : QObject(parent)
  33. , _userStatusConnector(userStatusConnector)
  34. , _userStatus("no-id", "", "😀", UserStatus::OnlineStatus::Online, false, {})
  35. , _dateTimeProvider(new DateTimeProvider)
  36. {
  37. _userStatus.setIcon("😀");
  38. init();
  39. }
  40. UserStatusSelectorModel::UserStatusSelectorModel(std::shared_ptr<UserStatusConnector> userStatusConnector,
  41. std::unique_ptr<DateTimeProvider> dateTimeProvider,
  42. QObject *parent)
  43. : QObject(parent)
  44. , _userStatusConnector(userStatusConnector)
  45. , _dateTimeProvider(std::move(dateTimeProvider))
  46. {
  47. _userStatus.setIcon("😀");
  48. init();
  49. }
  50. UserStatusSelectorModel::UserStatusSelectorModel(const UserStatus &userStatus,
  51. std::unique_ptr<DateTimeProvider> dateTimeProvider, QObject *parent)
  52. : QObject(parent)
  53. , _userStatus(userStatus)
  54. , _dateTimeProvider(std::move(dateTimeProvider))
  55. {
  56. _userStatus.setIcon("😀");
  57. }
  58. UserStatusSelectorModel::UserStatusSelectorModel(const UserStatus &userStatus,
  59. QObject *parent)
  60. : QObject(parent)
  61. , _userStatus(userStatus)
  62. {
  63. _userStatus.setIcon("😀");
  64. }
  65. void UserStatusSelectorModel::load(int id)
  66. {
  67. reset();
  68. qCDebug(lcUserStatusDialogModel) << "Loading user status connector for user with index: " << id;
  69. _userStatusConnector = UserModel::instance()->userStatusConnector(id);
  70. init();
  71. }
  72. void UserStatusSelectorModel::reset()
  73. {
  74. if (_userStatusConnector) {
  75. disconnect(_userStatusConnector.get(), &UserStatusConnector::userStatusFetched, this,
  76. &UserStatusSelectorModel::onUserStatusFetched);
  77. disconnect(_userStatusConnector.get(), &UserStatusConnector::predefinedStatusesFetched, this,
  78. &UserStatusSelectorModel::onPredefinedStatusesFetched);
  79. disconnect(_userStatusConnector.get(), &UserStatusConnector::error, this,
  80. &UserStatusSelectorModel::onError);
  81. disconnect(_userStatusConnector.get(), &UserStatusConnector::userStatusSet, this,
  82. &UserStatusSelectorModel::onUserStatusSet);
  83. disconnect(_userStatusConnector.get(), &UserStatusConnector::messageCleared, this,
  84. &UserStatusSelectorModel::onMessageCleared);
  85. }
  86. _userStatusConnector = nullptr;
  87. }
  88. void UserStatusSelectorModel::init()
  89. {
  90. if (!_userStatusConnector) {
  91. return;
  92. }
  93. connect(_userStatusConnector.get(), &UserStatusConnector::userStatusFetched, this,
  94. &UserStatusSelectorModel::onUserStatusFetched);
  95. connect(_userStatusConnector.get(), &UserStatusConnector::predefinedStatusesFetched, this,
  96. &UserStatusSelectorModel::onPredefinedStatusesFetched);
  97. connect(_userStatusConnector.get(), &UserStatusConnector::error, this,
  98. &UserStatusSelectorModel::onError);
  99. connect(_userStatusConnector.get(), &UserStatusConnector::userStatusSet, this,
  100. &UserStatusSelectorModel::onUserStatusSet);
  101. connect(_userStatusConnector.get(), &UserStatusConnector::messageCleared, this,
  102. &UserStatusSelectorModel::onMessageCleared);
  103. _userStatusConnector->fetchUserStatus();
  104. _userStatusConnector->fetchPredefinedStatuses();
  105. }
  106. void UserStatusSelectorModel::onUserStatusSet()
  107. {
  108. emit finished();
  109. }
  110. void UserStatusSelectorModel::onMessageCleared()
  111. {
  112. emit finished();
  113. }
  114. void UserStatusSelectorModel::onError(UserStatusConnector::Error error)
  115. {
  116. qCWarning(lcUserStatusDialogModel) << "Error:" << error;
  117. switch (error) {
  118. case UserStatusConnector::Error::CouldNotFetchPredefinedUserStatuses:
  119. setError(tr("Could not fetch predefined statuses. Make sure you are connected to the server."));
  120. return;
  121. case UserStatusConnector::Error::CouldNotFetchUserStatus:
  122. setError(tr("Could not fetch status. Make sure you are connected to the server."));
  123. return;
  124. case UserStatusConnector::Error::UserStatusNotSupported:
  125. setError(tr("Status feature is not supported. You will not be able to set your status."));
  126. return;
  127. case UserStatusConnector::Error::EmojisNotSupported:
  128. setError(tr("Emojis are not supported. Some status functionality may not work."));
  129. return;
  130. case UserStatusConnector::Error::CouldNotSetUserStatus:
  131. setError(tr("Could not set status. Make sure you are connected to the server."));
  132. return;
  133. case UserStatusConnector::Error::CouldNotClearMessage:
  134. setError(tr("Could not clear status message. Make sure you are connected to the server."));
  135. return;
  136. }
  137. Q_UNREACHABLE();
  138. }
  139. void UserStatusSelectorModel::setError(const QString &reason)
  140. {
  141. _errorMessage = reason;
  142. emit errorMessageChanged();
  143. }
  144. void UserStatusSelectorModel::clearError()
  145. {
  146. setError("");
  147. }
  148. void UserStatusSelectorModel::setOnlineStatus(UserStatus::OnlineStatus status)
  149. {
  150. if (!_userStatusConnector || status == _userStatus.state()) {
  151. return;
  152. }
  153. _userStatus.setState(status);
  154. _userStatusConnector->setUserStatus(_userStatus);
  155. emit onlineStatusChanged();
  156. }
  157. QUrl UserStatusSelectorModel::onlineIcon() const
  158. {
  159. return Theme::instance()->statusOnlineImageSource();
  160. }
  161. QUrl UserStatusSelectorModel::awayIcon() const
  162. {
  163. return Theme::instance()->statusAwayImageSource();
  164. }
  165. QUrl UserStatusSelectorModel::dndIcon() const
  166. {
  167. return Theme::instance()->statusDoNotDisturbImageSource();
  168. }
  169. QUrl UserStatusSelectorModel::invisibleIcon() const
  170. {
  171. return Theme::instance()->statusInvisibleImageSource();
  172. }
  173. UserStatus::OnlineStatus UserStatusSelectorModel::onlineStatus() const
  174. {
  175. return _userStatus.state();
  176. }
  177. QString UserStatusSelectorModel::userStatusMessage() const
  178. {
  179. return _userStatus.message();
  180. }
  181. void UserStatusSelectorModel::setUserStatusMessage(const QString &message)
  182. {
  183. _userStatus.setMessage(message);
  184. _userStatus.setMessagePredefined(false);
  185. emit userStatusChanged();
  186. }
  187. void UserStatusSelectorModel::setUserStatusEmoji(const QString &emoji)
  188. {
  189. _userStatus.setIcon(emoji);
  190. _userStatus.setMessagePredefined(false);
  191. emit userStatusChanged();
  192. }
  193. QString UserStatusSelectorModel::userStatusEmoji() const
  194. {
  195. return _userStatus.icon();
  196. }
  197. void UserStatusSelectorModel::onUserStatusFetched(const UserStatus &userStatus)
  198. {
  199. if (userStatus.state() != UserStatus::OnlineStatus::Offline) {
  200. _userStatus.setState(userStatus.state());
  201. }
  202. _userStatus.setMessage(userStatus.message());
  203. _userStatus.setMessagePredefined(userStatus.messagePredefined());
  204. _userStatus.setId(userStatus.id());
  205. _userStatus.setClearAt(userStatus.clearAt());
  206. if (!userStatus.icon().isEmpty()) {
  207. _userStatus.setIcon(userStatus.icon());
  208. }
  209. emit userStatusChanged();
  210. emit onlineStatusChanged();
  211. emit clearAtDisplayStringChanged();
  212. }
  213. Optional<ClearAt> UserStatusSelectorModel::clearStageTypeToDateTime(ClearStageType type) const
  214. {
  215. switch (type) {
  216. case ClearStageType::DontClear:
  217. return {};
  218. case ClearStageType::HalfHour: {
  219. ClearAt clearAt;
  220. clearAt._type = ClearAtType::Period;
  221. clearAt._period = 60 * 30;
  222. return clearAt;
  223. }
  224. case ClearStageType::OneHour: {
  225. ClearAt clearAt;
  226. clearAt._type = ClearAtType::Period;
  227. clearAt._period = 60 * 60;
  228. return clearAt;
  229. }
  230. case ClearStageType::FourHour: {
  231. ClearAt clearAt;
  232. clearAt._type = ClearAtType::Period;
  233. clearAt._period = 60 * 60 * 4;
  234. return clearAt;
  235. }
  236. case ClearStageType::Today: {
  237. ClearAt clearAt;
  238. clearAt._type = ClearAtType::EndOf;
  239. clearAt._endof = "day";
  240. return clearAt;
  241. }
  242. case ClearStageType::Week: {
  243. ClearAt clearAt;
  244. clearAt._type = ClearAtType::EndOf;
  245. clearAt._endof = "week";
  246. return clearAt;
  247. }
  248. default:
  249. Q_UNREACHABLE();
  250. }
  251. }
  252. void UserStatusSelectorModel::setUserStatus()
  253. {
  254. if (!_userStatusConnector) {
  255. return;
  256. }
  257. clearError();
  258. _userStatusConnector->setUserStatus(_userStatus);
  259. }
  260. void UserStatusSelectorModel::clearUserStatus()
  261. {
  262. if (!_userStatusConnector) {
  263. return;
  264. }
  265. clearError();
  266. _userStatusConnector->clearMessage();
  267. }
  268. void UserStatusSelectorModel::onPredefinedStatusesFetched(const QVector<UserStatus> &statuses)
  269. {
  270. _predefinedStatuses = statuses;
  271. emit predefinedStatusesChanged();
  272. }
  273. QVector<UserStatus> UserStatusSelectorModel::predefinedStatuses() const
  274. {
  275. return _predefinedStatuses;
  276. }
  277. void UserStatusSelectorModel::setPredefinedStatus(const UserStatus &predefinedStatus)
  278. {
  279. _userStatus.setMessagePredefined(true);
  280. _userStatus.setId(predefinedStatus.id());
  281. _userStatus.setMessage(predefinedStatus.message());
  282. _userStatus.setIcon(predefinedStatus.icon());
  283. _userStatus.setClearAt(predefinedStatus.clearAt());
  284. emit userStatusChanged();
  285. emit clearAtDisplayStringChanged();
  286. }
  287. QString UserStatusSelectorModel::clearAtStageToString(ClearStageType stage) const
  288. {
  289. switch (stage) {
  290. case ClearStageType::DontClear:
  291. return tr("Don't clear");
  292. case ClearStageType::HalfHour:
  293. return tr("30 minutes");
  294. case ClearStageType::OneHour:
  295. return tr("1 hour");
  296. case ClearStageType::FourHour:
  297. return tr("4 hours");
  298. case ClearStageType::Today:
  299. return tr("Today");
  300. case ClearStageType::Week:
  301. return tr("This week");
  302. default:
  303. Q_UNREACHABLE();
  304. }
  305. }
  306. QVariantList UserStatusSelectorModel::clearStageTypes() const
  307. {
  308. QVariantList clearStageTypes;
  309. for(const auto clearStageType : _clearStages) {
  310. QVariantMap clearStageToAdd;
  311. clearStageToAdd.insert(QStringLiteral("display"), clearAtStageToString(clearStageType));
  312. clearStageToAdd.insert(QStringLiteral("clearStageType"), QVariant::fromValue(clearStageType));
  313. clearStageTypes.append(clearStageToAdd);
  314. }
  315. return clearStageTypes;
  316. }
  317. void UserStatusSelectorModel::setClearAt(const ClearStageType clearStageType)
  318. {
  319. _userStatus.setClearAt(clearStageTypeToDateTime(clearStageType));
  320. emit clearAtDisplayStringChanged();
  321. }
  322. QString UserStatusSelectorModel::errorMessage() const
  323. {
  324. return _errorMessage;
  325. }
  326. QString UserStatusSelectorModel::timeDifferenceToString(int differenceSecs) const
  327. {
  328. if (differenceSecs < 60) {
  329. return tr("Less than a minute");
  330. } else if (differenceSecs < 60 * 60) {
  331. const auto minutesLeft = std::ceil(differenceSecs / 60.0);
  332. if (minutesLeft == 1) {
  333. return tr("1 minute");
  334. } else {
  335. return tr("%1 minutes").arg(minutesLeft);
  336. }
  337. } else if (differenceSecs < 60 * 60 * 24) {
  338. const auto hoursLeft = std::ceil(differenceSecs / 60.0 / 60.0);
  339. if (hoursLeft == 1) {
  340. return tr("1 hour");
  341. } else {
  342. return tr("%1 hours").arg(hoursLeft);
  343. }
  344. } else {
  345. const auto daysLeft = std::ceil(differenceSecs / 60.0 / 60.0 / 24.0);
  346. if (daysLeft == 1) {
  347. return tr("1 day");
  348. } else {
  349. return tr("%1 days").arg(daysLeft);
  350. }
  351. }
  352. }
  353. QString UserStatusSelectorModel::clearAtReadable(const UserStatus &status) const
  354. {
  355. const auto clearAt = status.clearAt();
  356. return clearAtReadable(clearAt);
  357. }
  358. QString UserStatusSelectorModel::clearAtReadable(const Optional<ClearAt> &clearAt) const
  359. {
  360. if (clearAt) {
  361. switch (clearAt->_type) {
  362. case ClearAtType::Period: {
  363. return timeDifferenceToString(clearAt->_period);
  364. }
  365. case ClearAtType::Timestamp: {
  366. const int difference = static_cast<int>(clearAt->_timestamp - _dateTimeProvider->currentDateTime().toSecsSinceEpoch());
  367. return timeDifferenceToString(difference);
  368. }
  369. case ClearAtType::EndOf: {
  370. if (clearAt->_endof == "day") {
  371. return tr("Today");
  372. } else if (clearAt->_endof == "week") {
  373. return tr("This week");
  374. }
  375. Q_UNREACHABLE();
  376. }
  377. default:
  378. Q_UNREACHABLE();
  379. }
  380. }
  381. return tr("Don't clear");
  382. }
  383. QString UserStatusSelectorModel::clearAtDisplayString() const
  384. {
  385. return clearAtReadable(_userStatus.clearAt());
  386. }
  387. }