diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Cache.cc | 45 | ||||
-rw-r--r-- | src/ChatPage.cc | 38 | ||||
-rw-r--r-- | src/MatrixClient.cc | 38 | ||||
-rw-r--r-- | src/Utils.cc | 27 | ||||
-rw-r--r-- | src/timeline/TimelineView.cc | 20 |
5 files changed, 146 insertions, 22 deletions
diff --git a/src/Cache.cc b/src/Cache.cc index 92c86322..8b00a828 100644 --- a/src/Cache.cc +++ b/src/Cache.cc @@ -48,6 +48,7 @@ static constexpr const char *MEDIA_DB = "media"; static constexpr const char *SYNC_STATE_DB = "sync_state"; //! Read receipts per room/event. static constexpr const char *READ_RECEIPTS_DB = "read_receipts"; +static constexpr const char *NOTIFICATIONS_DB = "sent_notifications"; using CachedReceipts = std::multimap<uint64_t, std::string, std::greater<uint64_t>>; using Receipts = std::map<std::string, std::map<std::string, uint64_t>>; @@ -60,6 +61,7 @@ Cache::Cache(const QString &userId, QObject *parent) , invitesDb_{0} , mediaDb_{0} , readReceiptsDb_{0} + , notificationsDb_{0} , localUserId_{userId} {} @@ -112,12 +114,13 @@ Cache::setup() env_.open(statePath.toStdString().c_str()); } - auto txn = lmdb::txn::begin(env_); - syncStateDb_ = lmdb::dbi::open(txn, SYNC_STATE_DB, MDB_CREATE); - roomsDb_ = lmdb::dbi::open(txn, ROOMS_DB, MDB_CREATE); - invitesDb_ = lmdb::dbi::open(txn, INVITES_DB, MDB_CREATE); - mediaDb_ = lmdb::dbi::open(txn, MEDIA_DB, MDB_CREATE); - readReceiptsDb_ = lmdb::dbi::open(txn, READ_RECEIPTS_DB, MDB_CREATE); + auto txn = lmdb::txn::begin(env_); + syncStateDb_ = lmdb::dbi::open(txn, SYNC_STATE_DB, MDB_CREATE); + roomsDb_ = lmdb::dbi::open(txn, ROOMS_DB, MDB_CREATE); + invitesDb_ = lmdb::dbi::open(txn, INVITES_DB, MDB_CREATE); + mediaDb_ = lmdb::dbi::open(txn, MEDIA_DB, MDB_CREATE); + readReceiptsDb_ = lmdb::dbi::open(txn, READ_RECEIPTS_DB, MDB_CREATE); + notificationsDb_ = lmdb::dbi::open(txn, NOTIFICATIONS_DB, MDB_CREATE); txn.commit(); qRegisterMetaType<RoomInfo>(); @@ -1087,6 +1090,36 @@ Cache::getMembers(const std::string &room_id, std::size_t startIndex, std::size_ return members; } +void +Cache::markSentNotification(const std::string &event_id) +{ + auto txn = lmdb::txn::begin(env_); + lmdb::dbi_put(txn, notificationsDb_, lmdb::val(event_id), lmdb::val(std::string(""))); + txn.commit(); +} + +void +Cache::removeReadNotification(const std::string &event_id) +{ + auto txn = lmdb::txn::begin(env_); + + lmdb::dbi_del(txn, notificationsDb_, lmdb::val(event_id), nullptr); + + txn.commit(); +} + +bool +Cache::isNotificationSent(const std::string &event_id) +{ + auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); + + lmdb::val value; + bool res = lmdb::dbi_get(txn, notificationsDb_, lmdb::val(event_id), value); + txn.commit(); + + return res; +} + QHash<QString, QString> Cache::DisplayNames; QHash<QString, QString> Cache::AvatarUrls; diff --git a/src/ChatPage.cc b/src/ChatPage.cc index ee338c2d..4750e67a 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -36,6 +36,7 @@ #include "TypingDisplay.h" #include "UserInfoWidget.h" #include "UserSettingsPage.h" +#include "Utils.h" #include "dialogs/ReadReceipts.h" #include "timeline/TimelineViewManager.h" @@ -339,6 +340,10 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, connect(client_.data(), &MatrixClient::redactionFailed, this, [this](const QString &error) { emit showNotification(QString("Message redaction failed: %1").arg(error)); }); + connect(client_.data(), + &MatrixClient::notificationsRetrieved, + this, + &ChatPage::sendDesktopNotifications); showContentTimer_ = new QTimer(this); showContentTimer_->setSingleShot(true); @@ -420,13 +425,20 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, view_manager_->initialize(rooms); removeLeftRooms(rooms.leave); + bool hasNotifications = false; for (const auto &room : rooms.join) { auto room_id = QString::fromStdString(room.first); updateTypingUsers(room_id, room.second.ephemeral.typing); updateRoomNotificationCount( room_id, room.second.unread_notifications.notification_count); + + if (room.second.unread_notifications.notification_count > 0) + hasNotifications = true; } + + if (hasNotifications) + client_->getNotifications(); }); connect(this, &ChatPage::syncRoomlist, room_list_, &RoomList::sync); @@ -838,3 +850,29 @@ ChatPage::updateRoomNotificationCount(const QString &room_id, uint16_t notificat { room_list_->updateUnreadMessageCount(room_id, notification_count); } + +void +ChatPage::sendDesktopNotifications(const mtx::responses::Notifications &res) +{ + for (const auto &item : res.notifications) { + const auto event_id = utils::event_id(item.event); + + try { + if (item.read) { + cache_->removeReadNotification(event_id); + continue; + } + + if (!cache_->isNotificationSent(event_id)) { + // TODO: send desktop notification + // qDebug() << "sender" << utils::event_sender(item.event); + // qDebug() << "body" << utils::event_body(item.event); + + // We should only sent one notification per event. + // cache_->markSentNotification(event_id); + } + } catch (const lmdb::error &e) { + qWarning() << e.what(); + } + } +} diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc index de930fc3..54756c7c 100644 --- a/src/MatrixClient.cc +++ b/src/MatrixClient.cc @@ -1310,3 +1310,41 @@ MatrixClient::redactEvent(const QString &room_id, const QString &event_id) } }); } + +void +MatrixClient::getNotifications() noexcept +{ + QUrlQuery query; + query.addQueryItem("limit", "5"); + + QUrl endpoint(server_); + endpoint.setQuery(query); + endpoint.setPath(clientApiUrl_ + "/notifications"); + + QNetworkRequest request(QString(endpoint.toEncoded())); + setupAuth(request); + + auto reply = get(request); + connect(reply, &QNetworkReply::finished, this, [reply, this]() { + reply->deleteLater(); + + int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + auto data = reply->readAll(); + + if (status == 0 || status >= 400) { + try { + mtx::errors::Error res = nlohmann::json::parse(data); + std::cout << nlohmann::json::parse(data).dump(2) << '\n'; + // TODO: Response with an error signal + return; + } catch (const std::exception &) { + } + } + + try { + emit notificationsRetrieved(nlohmann::json::parse(data)); + } catch (const std::exception &e) { + qWarning() << "failed to parse /notifications response" << e.what(); + } + }); +} diff --git a/src/Utils.cc b/src/Utils.cc index d9b06b52..14620145 100644 --- a/src/Utils.cc +++ b/src/Utils.cc @@ -111,3 +111,30 @@ utils::levenshtein_distance(const std::string &s1, const std::string &s2) return *std::min_element(row1.begin(), row1.end()); } + +QString +utils::event_body(const mtx::events::collections::TimelineEvents &event) +{ + using namespace mtx::events; + using namespace mtx::events::msg; + + if (mpark::holds_alternative<RoomEvent<Audio>>(event)) { + return message_body<RoomEvent<Audio>>(event); + } else if (mpark::holds_alternative<RoomEvent<Emote>>(event)) { + return message_body<RoomEvent<Emote>>(event); + } else if (mpark::holds_alternative<RoomEvent<File>>(event)) { + return message_body<RoomEvent<File>>(event); + } else if (mpark::holds_alternative<RoomEvent<Image>>(event)) { + return message_body<RoomEvent<Image>>(event); + } else if (mpark::holds_alternative<RoomEvent<Notice>>(event)) { + return message_body<RoomEvent<Notice>>(event); + } else if (mpark::holds_alternative<Sticker>(event)) { + return message_body<Sticker>(event); + } else if (mpark::holds_alternative<RoomEvent<Text>>(event)) { + return message_body<RoomEvent<Text>>(event); + } else if (mpark::holds_alternative<RoomEvent<Video>>(event)) { + return message_body<RoomEvent<Video>>(event); + } + + return QString(); +} diff --git a/src/timeline/TimelineView.cc b/src/timeline/TimelineView.cc index 8781f90e..5b433674 100644 --- a/src/timeline/TimelineView.cc +++ b/src/timeline/TimelineView.cc @@ -727,12 +727,6 @@ TimelineView::event(QEvent *event) return QWidget::event(event); } -QString -TimelineView::getEventSender(const mtx::events::collections::TimelineEvents &event) const -{ - return mpark::visit([](auto msg) { return QString::fromStdString(msg.sender); }, event); -} - void TimelineView::toggleScrollDownButton() { @@ -826,8 +820,8 @@ TimelineView::relativeWidget(TimelineItem *item, int dt) const TimelineEvent TimelineView::findFirstViewableEvent(const std::vector<TimelineEvent> &events) { - auto it = std::find_if(events.begin(), events.end(), [this](const auto &event) { - return mtx::events::EventType::RoomMessage == getEventType(event); + auto it = std::find_if(events.begin(), events.end(), [](const auto &event) { + return mtx::events::EventType::RoomMessage == utils::event_type(event); }); return (it == std::end(events)) ? events.front() : *it; @@ -836,19 +830,13 @@ TimelineView::findFirstViewableEvent(const std::vector<TimelineEvent> &events) TimelineEvent TimelineView::findLastViewableEvent(const std::vector<TimelineEvent> &events) { - auto it = std::find_if(events.rbegin(), events.rend(), [this](const auto &event) { - return mtx::events::EventType::RoomMessage == getEventType(event); + auto it = std::find_if(events.rbegin(), events.rend(), [](const auto &event) { + return mtx::events::EventType::RoomMessage == utils::event_type(event); }); return (it == std::rend(events)) ? events.back() : *it; } -inline mtx::events::EventType -TimelineView::getEventType(const mtx::events::collections::TimelineEvents &event) const -{ - return mpark::visit([](auto msg) { return msg.type; }, event); -} - void TimelineView::saveMessageInfo(const QString &sender, uint64_t origin_server_ts, |